【问题标题】:Customizing spree's product controller create action自定义 spree 的产品控制器创建操作
【发布时间】:2015-02-20 03:13:39
【问题描述】:

我有一个关于 Spree 的非常具体的定制问题。我希望为其核心功能添加多租户。我的应用需要进行以下设置:

  1. 可以同时通过网站买卖产品的用户(就像 craigslist)。

  2. 用户成为供应商,因为他们被关联为他们列出的待售产品的所有者。

  3. 会有一个产品索引页面,可以参考每个 Spree::Product。当您点击特定产品时,它会告诉您卖家是谁,您可以快速导航到该卖家资料页面并查看该卖家的产品。

  4. 每个用户都可以管理他们销售的产品:添加产品图片、更改产品描述、查看谁订购了他们的产品,当然还可以从这些交易中获利。

简单地说,尝试实现这一点是一个挑战。我是一个相对较新的Rails。我已经调查并实施(有些成功)狂欢multi-tenant gem。它似乎使事情变得过于复杂,并且仅适用于 Spree 2.2。

我认为实现这一点的最佳方法是在产品/创建操作上使用类 eval 方法。这是一些让我相信这一点的 Spree 文档:link

我希望得到您的帮助,让我深入了解如何执行此操作。或者,如果这没有意义,有什么更好的方法来做到这一点?

我已经完成了一些工作:我在产品模型装饰器和我的 spree 用户类中添加了必要的关联。我已经创建并运行了迁移以将 user_id 列添加到 spree::products 中。

我现在需要做的是编写代码,将 current_user 的 id 分配给他们创建的产品的列。我不确定如何做到这一点。基本上,我不确切知道我需要重写哪些代码才能进行此更改而不破坏其他任何内容。

感谢您提前回复。我很高兴澄清我遗漏的任何内容。

以下是产品控制器 spree_backend gem 中的代码:

module Spree
module Admin
class ProductsController < ResourceController
  helper 'spree/products'

  before_filter :load_data, :except => :index
  create.before :create_before
  update.before :update_before
  helper_method :clone_object_url

  def show
    session[:return_to] ||= request.referer
    redirect_to( :action => :edit )
  end

  def index
    session[:return_to] = request.url
    respond_with(@collection)
  end

  def update
    if params[:product][:taxon_ids].present?
      params[:product][:taxon_ids] = params[:product][:taxon_ids].split(',')
    end
    if params[:product][:option_type_ids].present?
      params[:product][:option_type_ids] = params[:product][:option_type_ids].split(',')
    end
    invoke_callbacks(:update, :before)
    if @object.update_attributes(permitted_resource_params)
      invoke_callbacks(:update, :after)
      flash[:success] = flash_message_for(@object, :successfully_updated)
      respond_with(@object) do |format|
        format.html { redirect_to location_after_save }
        format.js   { render :layout => false }
      end
    else
      # Stops people submitting blank slugs, causing errors when they try to update the product again
      @product.slug = @product.slug_was if @product.slug.blank?
      invoke_callbacks(:update, :fails)
      respond_with(@object)
    end
  end

  def destroy
    @product = Product.friendly.find(params[:id])
    @product.destroy

    flash[:success] = Spree.t('notice_messages.product_deleted')

    respond_with(@product) do |format|
      format.html { redirect_to collection_url }
      format.js  { render_js_for_destroy }
    end
  end

  def clone
    @new = @product.duplicate

    if @new.save
      flash[:success] = Spree.t('notice_messages.product_cloned')
    else
      flash[:error] = Spree.t('notice_messages.product_not_cloned')
    end

    redirect_to edit_admin_product_url(@new)
  end

  def stock
    @variants = @product.variants
    @variants = [@product.master] if @variants.empty?
    @stock_locations = StockLocation.accessible_by(current_ability, :read)
    if @stock_locations.empty?
      flash[:error] = Spree.t(:stock_management_requires_a_stock_location)
      redirect_to admin_stock_locations_path
    end
  end

  protected

    def find_resource
      Product.with_deleted.friendly.find(params[:id])
    end

    def location_after_save
      spree.edit_admin_product_url(@product)
    end

    def load_data
      @taxons = Taxon.order(:name)
      @option_types = OptionType.order(:name)
      @tax_categories = TaxCategory.order(:name)
      @shipping_categories = ShippingCategory.order(:name)
    end

    def collection
      return @collection if @collection.present?
      params[:q] ||= {}
      params[:q][:deleted_at_null] ||= "1"

      params[:q][:s] ||= "name asc"
      @collection = super
      @collection = @collection.with_deleted if params[:q].delete(:deleted_at_null) == '0'
      # @search needs to be defined as this is passed to search_form_for
      @search = @collection.ransack(params[:q])
      @collection = @search.result.
            distinct_by_product_ids(params[:q][:s]).
            includes(product_includes).
            page(params[:page]).
            per(Spree::Config[:admin_products_per_page])

      @collection
    end

    def create_before
      return if params[:product][:prototype_id].blank?
      @prototype = Spree::Prototype.find(params[:product][:prototype_id])
    end

    def update_before
      # note: we only reset the product properties if we're receiving a post from the form on that tab
      return unless params[:clear_product_properties]
      params[:product] ||= {}
    end

    def product_includes
      [{ :variants => [:images], :master => [:images, :default_price]}]
    end

    def clone_object_url resource
      clone_admin_product_url resource
    end

    def permit_attributes
      params.require(:product).permit!
    end
end

结束 结束

您会注意到此产品控制器中没有创建操作。它从这里的资源控制器继承它:

class Spree::Admin::ResourceController < Spree::Admin::BaseController


include Spree::Backend::Callbacks

  helper_method :new_object_url, :edit_object_url, :object_url, :collection_url
  before_filter :load_resource, :except => [:update_positions]
  rescue_from ActiveRecord::RecordNotFound, :with => :resource_not_found

  respond_to :html

  def new
    invoke_callbacks(:new_action, :before)
    respond_with(@object) do |format|
      format.html { render :layout => !request.xhr? }
      if request.xhr?
        format.js   { render :layout => false }
      end
    end
  end

  def edit
    respond_with(@object) do |format|
      format.html { render :layout => !request.xhr? }
      if request.xhr?
        format.js   { render :layout => false }
      end
    end
  end

  def update
    invoke_callbacks(:update, :before)
    if @object.update_attributes(permitted_resource_params)
      invoke_callbacks(:update, :after)
      flash[:success] = flash_message_for(@object, :successfully_updated)
      respond_with(@object) do |format|
        format.html { redirect_to location_after_save }
        format.js   { render :layout => false }
      end
    else
      invoke_callbacks(:update, :fails)
      respond_with(@object) do |format|
        format.html do
          flash.now[:error] = @object.errors.full_messages.join(", ")
          render action: 'edit'
        end
        format.js { render layout: false }
      end
    end
  end

  def create
    invoke_callbacks(:create, :before)
    @object.attributes = permitted_resource_params
    if @object.save
      invoke_callbacks(:create, :after)
      flash[:success] = flash_message_for(@object, :successfully_created)
      respond_with(@object) do |format|
        format.html { redirect_to location_after_save }
        format.js   { render :layout => false }
      end
    else
      invoke_callbacks(:create, :fails)
      respond_with(@object) do |format|
        format.html do
          flash.now[:error] = @object.errors.full_messages.join(", ")
          render action: 'new'
        end
        format.js { render layout: false }
      end
    end
  end

  def update_positions
    params[:positions].each do |id, index|
      model_class.find(id).update_attributes(:position => index)
    end

    respond_to do |format|
      format.js  { render :text => 'Ok' }
    end
  end

  def destroy
    invoke_callbacks(:destroy, :before)
    if @object.destroy
      invoke_callbacks(:destroy, :after)
      flash[:success] = flash_message_for(@object, :successfully_removed)
      respond_with(@object) do |format|
        format.html { redirect_to location_after_destroy }
        format.js   { render :partial => "spree/admin/shared/destroy" }
      end
    else
      invoke_callbacks(:destroy, :fails)
      respond_with(@object) do |format|
        format.html { redirect_to location_after_destroy }
      end
    end
  end

  protected

    class << self
      attr_accessor :parent_data

      def belongs_to(model_name, options = {})
        @parent_data ||= {}
        @parent_data[:model_name] = model_name
        @parent_data[:model_class] = model_name.to_s.classify.constantize
        @parent_data[:find_by] = options[:find_by] || :id
      end
    end

    def resource_not_found
      flash[:error] = flash_message_for(model_class.new, :not_found)
      redirect_to collection_url
    end

    def model_class
      "Spree::#{controller_name.classify}".constantize
    end

    def model_name
      parent_data[:model_name].gsub('spree/', '')
    end

    def object_name
      controller_name.singularize
    end

    def load_resource
      if member_action?
        @object ||= load_resource_instance

        # call authorize! a third time (called twice already in Admin::BaseController)
        # this time we pass the actual instance so fine-grained abilities can control
        # access to individual records, not just entire models.
        authorize! action, @object

        instance_variable_set("@#{object_name}", @object)
      else
        @collection ||= collection

        # note: we don't call authorize here as the collection method should use
        # CanCan's accessible_by method to restrict the actual records returned

        instance_variable_set("@#{controller_name}", @collection)
      end
    end

    def load_resource_instance
      if new_actions.include?(action)
        build_resource
      elsif params[:id]
        find_resource
      end
    end

    def parent_data
      self.class.parent_data
    end

    def parent
      if parent_data.present?
        @parent ||= parent_data[:model_class].send("find_by_#{parent_data[:find_by]}", params["#{model_name}_id"])
        instance_variable_set("@#{model_name}", @parent)
      else
        nil
      end
    end

    def find_resource
      if parent_data.present?
        parent.send(controller_name).find(params[:id])
      else
        model_class.find(params[:id])
      end
    end

    def build_resource
      if parent_data.present?
        parent.send(controller_name).build
      else
        model_class.new
      end
    end

    def collection
      return parent.send(controller_name) if parent_data.present?
      if model_class.respond_to?(:accessible_by) && !current_ability.has_block?(params[:action], model_class)
        model_class.accessible_by(current_ability, action)
      else
        model_class.where(nil)
      end
    end

    def location_after_destroy
      collection_url
    end

    def location_after_save
      collection_url
    end

    # URL helpers

    def new_object_url(options = {})
      if parent_data.present?
        spree.new_polymorphic_url([:admin, parent, model_class], options)
      else
        spree.new_polymorphic_url([:admin, model_class], options)
      end
    end

    def edit_object_url(object, options = {})
      if parent_data.present?
        spree.send "edit_admin_#{model_name}_#{object_name}_url", parent, object, options
      else
        spree.send "edit_admin_#{object_name}_url", object, options
      end
    end

    def object_url(object = nil, options = {})
      target = object ? object : @object
      if parent_data.present?
        spree.send "admin_#{model_name}_#{object_name}_url", parent, target, options
      else
        spree.send "admin_#{object_name}_url", target, options
      end
    end

    def collection_url(options = {})
      if parent_data.present?
        spree.polymorphic_url([:admin, parent, model_class], options)
      else
        spree.polymorphic_url([:admin, model_class], options)
      end
    end

    # Allow all attributes to be updatable.
    #
    # Other controllers can, should, override it to set custom logic
    def permitted_resource_params
      params.require(object_name).permit!
    end

    def collection_actions
      [:index]
    end

    def member_action?
      !collection_actions.include? action
    end

    def new_actions
      [:new, :create]
    end
end

【问题讨论】:

    标签: ruby-on-rails spree


    【解决方案1】:

    你应该试试Spree Admin Roles and Acess Gem,它可以轻松完成大部分你想做的事情。

    将它添加到您的 Gemfile -> gem 'spree_admin_roles_and_access' 然后安装它并运行安装生成器:

    $ bundle install
    $ rails generate spree_admin_roles_and_access:install
    $ rake spree_roles:permissions:populate # To populate user and admin roles with their permissions
    $ rake spree_roles:permissions:populate_permission_sets # To set up some convenient permission sets.
    

    之后每个Spree::User会有一个权限设置

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-22
      相关资源
      最近更新 更多