3

我在为 Spree 2.0.4 定义新的动态 ProductFilters 时遇到问题

我的客户有类别(例如分类单元)。一个产品属于一个类别,最多有 8 个属性,但属性还取决于每个产品所在的类别,并且属性的位置也很重要。

我的解决方案是以非规范化的方式扩展数据库:

module Spree
  Taxon.class_eval do


    belongs_to :prop1,  class_name: "Spree::Property",
                        foreign_key: "p1_id"
    belongs_to :prop2,  class_name: "Spree::Property",
                        foreign_key: "p2_id"
    belongs_to :prop3,  class_name: "Spree::Property",
                        foreign_key: "p3_id"
    belongs_to :prop4,  class_name: "Spree::Property",
                        foreign_key: "p4_id"
    belongs_to :prop5,  class_name: "Spree::Property",
                        foreign_key: "p5_id"
    belongs_to :prop6,  class_name: "Spree::Property",
                        foreign_key: "p6_id"
    belongs_to :prop7,  class_name: "Spree::Property",
                        foreign_key: "p7_id"
    belongs_to :prop8,  class_name: "Spree::Property",
                        foreign_key: "p8_id"

    attr_accessible :prop1, :prop2, :prop3, :prop4, :prop5, :prop6, :prop7, :prop8


    def properties
      prop = []
      prop << prop1
      prop << prop2
      prop << prop3
      prop << prop4
      prop << prop5
      prop << prop6
      prop << prop7
      prop << prop8
      return prop
    end

    def applicable_filters
      fs = []
      fs << Spree::Core::ProductFilters.price_filter if Spree::Core::ProductFilters.respond_to?(:price_filter)
      #fs << Spree::Core::ProductFilters.brand_filter if Spree::Core::ProductFilters.respond_to?(:brand_filter)          
      fs
    end


  end
end

这样我就能够在相应产品的分类单元中获得可能的属性。我现在为每个分类属性(prop1 .. prop8)制作了 8 个过滤器,因为有些值是数字的,应该与文本值不同的处理方式,所以即使这不是完全干燥的,我也得出了以下解决方案:

module Spree
  module Core
    module ProductFilters

      if Spree::Property.table_exists?
        Spree::Product.add_search_scope :selective_prop1_any do |*opts|
          conds = opts.map {|o| ProductFilters.selective_prop1_filter[:conds][o]}.reject {|c| c.nil?}
          scope = conds.shift
          conds.each do |new_scope|
            scope = scope.or(new_scope)
          end
          Spree::Product.where(scope)
        end

        def ProductFilters.selective_prop1_filter(taxon = nil, locale = 'en')
          return if taxon.nil? #||= Spree::Taxon.find_by_permalink!("categories")
          property = taxon.prop1
          scope = Spree::ProductProperty.where(:property_id => property, :locale => locale).
                                         joins(:product => :taxons).
                                         where("#{Spree::Taxon.table_name}.id" => [taxon] + taxon.descendants).
                                         scoped
          brands = scope.pluck(:value).uniq
          {
            :name   => property.presentation,
            :scope  => :selective_prop1_any,
            :labels => brands.sort.map { |k| [k,k] }
          }
        end
      end

      if Spree::Property.table_exists?
        Spree::Product.add_search_scope :selective_prop2_any do |*opts|
          conds = opts.map {|o| ProductFilters.selective_prop2_filter[:conds][o]}.reject {|c| c.nil?}
          Rails.logger.debug conds.inspect
          scope = conds.shift
          Rails.logger.debug scope.inspect
          conds.each do |new_scope|
            scope = scope.or(new_scope)
          end
          Rails.logger.debug scope.inspect
          Spree::Product.where(scope)
        end

# ... other filters cut out for brevity

      if Spree::Property.table_exists?
        Spree::Product.add_search_scope :selective_prop8_any do |*opts|
          [..]
        end

        def ProductFilters.selective_prop8_filter(taxon = nil, locale = 'en')
          [..]
        end
      end

      Spree::Product.add_search_scope :price_range_any do |*opts|
        conds = opts.map {|o| Spree::Core::ProductFilters.price_filter[:conds][o]}.reject {|c| c.nil?}
        scope = conds.shift
        conds.each do |new_scope|
          scope = scope.or(new_scope)
        end
        Spree::Product.joins(:master => :default_price).where(scope)
      end

      def ProductFilters.format_price(amount)
        Spree::Money.new(amount)
      end

      def ProductFilters.price_filter
        v = Spree::Price.arel_table
        conds = [ [ Spree.t(:under_price, :price => format_price(10))   , v[:amount].lteq(10)],
                  [ "#{format_price(10)} - #{format_price(15)}"        , v[:amount].in(10..15)],
                  [ "#{format_price(15)} - #{format_price(18)}"        , v[:amount].in(15..18)],
                  [ "#{format_price(18)} - #{format_price(20)}"        , v[:amount].in(18..20)],
                  [ Spree.t(:or_over_price, :price => format_price(20)) , v[:amount].gteq(20)]]
        { :name   => Spree.t(:price_range),
          :scope  => :price_range_any,
          :conds  => Hash[*conds.flatten],
          :labels => conds.map {|k,v| [k,k]}
        }
      end
    end
  end
end

由于这个事实,即使是属性的值也应该本地化,因此在 ProductProperties 表中创建了一个列区域设置。我的选择性过滤器通过语言环境变量来检索正确的 ProductProperty。

由于 MVC 限制无法将区域设置从 session[:locale] 和当前分类单元传递给模型,我覆盖了原始显示逻辑,该逻辑使用 CONTROLLER(!) 中分类单元的 apply_filters 方法,如下所示:

TaxonsController.class_eval do
    def show
      @taxon = Taxon.find_by_permalink(params[:id])

      return unless @taxon

      if @taxon
        @filters = []
        @filters << Spree::Core::ProductFilters.selective_prop1_filter(@taxon, locale) unless @taxon.prop1.nil?
        [...]
        @filters << Spree::Core::ProductFilters.selective_prop8_filter(@taxon, locale) unless @taxon.prop8.nil?
        @filters.concat(@taxon.applicable_filters)
      else
        @filters = Spree::Core::ProductFilters.all_taxons
      end
      p = params.merge(:taxon => @taxon.id)
      @searcher = Spree::Config.searcher_class.new(params)
      @searcher.current_user = try_spree_current_user
      @searcher.current_currency = current_currency

      @products = @searcher.retrieve_products
    end
  end

这是主要留下的原始视图代码:

<% unless @filters.empty? %>
  <%= form_tag '', :method => :get, :id => 'sidebar_products_search' do %>
    <% params[:search] ||= {} %>
    <%= hidden_field_tag 'per_page', params[:per_page] %>
    <% @filters.each do |filter| %>
      <% labels = filter[:labels] || filter[:conds].map {|m,c| [m,m]} %>
      <% next if labels.empty? %>
      <div class="" data-hook="navigation">
        <h5 class="filter-title"> <%= filter[:name] %> </h5>
          <% labels.each do |nm,val| %>
            <% label = "#{filter[:name]}_#{nm}".gsub(/\s+/,'_') %>
            <label for="<%= label %>" class="checkbox" style="display:block;"><%= nm %><input type="checkbox"
                   id="<%= label %>"
                   name="search[<%= filter[:scope].to_s %>][]"
                   value="<%= val %>"
                   <%= params[:search][filter[:scope]] && params[:search][filter[:scope]].include?(val.to_s) ? "checked" : "" %> />
            </label>
          <% end %>
      </div>
    <% end %>
    <%= submit_tag Spree.t(:search), :name => nil, :class => 'button' %>
  <% end %>
<% end %>

显示的工作原理如下:根据用户所在的分类单元,会话 [:locale] 中 ProductProperties 的值被正确获取并显示,但现在出现了问题:

搜索不再起作用。即使未修改的 :price_range_any 过滤器也不起作用。始终显示属于当前分类单元的所有产品。params hash 中的搜索散列由以下形式正确构建search => {"price_range_any":["10.00 € EUR - 15.00 € EUR"]}

我不知道。如果我通过删除覆盖切换回原始文件,一切正常。

如何让我的过滤器正常运行?

4

0 回答 0