我对Spree有一个非常具体的定制问题。我希望将多租户添加到其核心功能中。我的应用需要进行以下设置:
可以通过网站同时买卖产品的用户(就像craigslist一样)。
用户成为供应商,因为他们与他们列出待售的产品的所有者相关联。
将有一个产品索引页面可以引用每个Spree :: Product。当您点击特定产品时,它会告诉您卖家是谁,您可以快速导航到该卖家个人资料页面并查看该卖家的产品。
每个用户都可以管理他们销售的产品:添加产品图片,更改产品描述,查看谁订购了他们的产品,当然还可以从这些交易中获取资金。
轻描淡写地试图实现这一点一直是一个挑战。我是铁路的新手。我已经调查并实现了(有些成功)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
答案 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