自定义spree的产品控制器创建操作

时间:2014-12-22 01:43:33

标签: ruby-on-rails spree

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

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

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

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

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

  5. 轻描淡写地试图实现这一点一直是一个挑战。我是铁路的新手。我已经调查并实现了(有些成功)spree multi-tenant gem。它似乎使事情过于复杂,它只适用于Spree 2.2。

    我认为最好的方法是在product / create动作上使用class eval方法。这里有一些Spree文档让我相信这一点:link

    我希望您能帮助我了解如何做到这一点。或者,如果这没有意义,那么最好的方法是做什么呢?

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

    我现在需要做的是编写代码以将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
    

1 个答案:

答案 0 :(得分:0)

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

将其添加到您的Gemfile - &gt; 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

中都会有权限设置