获取所有类别和子类别的产品(rails,awesome_nested_set)

时间:2011-09-07 10:57:42

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord nested-sets

正在开发正在开发的电子商务应用程序我正试图解决以下问题: 我通过awesome_nested_set插件实现了我的类别。 如果我通过选择一个类别列出我的文章一切正常,但对于某些链接,我想显示一个类别的所有产品及其子类别的产品。

这里的控制器代码仅适用于一个类别:

   # products_controller.rb
   def index
    if params[:category]
      @category = Category.find(params[:category])
      #@products = @category.product_list
      @products = @category.products
    else
      @category = false
      @products = Product.scoped
    end
    @products = @products.where("title like ?", "%" + params[:title] + "%") if params[:title]
    @products = @products.order("created_at).page(params[:page]).per( params[:per_page] ? params[:per_page] : 25)
    @categories = Category.all
  end

我注释掉的一行是我在类别模型中自己编写的辅助方法,它返回类别中所有产品及其子类别的数组。

定义如下:

# app/models/category.rb
def product_list
  self_and_ancestors.to_a.collect! { |x| x.products }
end

现在,当我取消注释此行并尝试选择一个类别时,我的产品控制器代码会出现错误,例如

undefined method `order' for #<Array:0x1887c2c>

undefined method `page' for #<Array:0x1887c2c>

因为我正在使用订购和分页,所以它不能再订购。

如何在控制器中的ActiveRecord Relation元素中获取所有产品? 感谢

更新

所以,当我使用以下内容时:

class Category < ActiveRecord::Base
    acts_as_nested_set

    attr_accessible :name, :description, :lft, :rgt, :parent_id   

    has_many :categorizations
    has_many :products, :through => :categorizations

    attr_accessor :product_list

    def branch_ids
      self_and_descendants.map(&:id).uniq 
    end

    def all_products
       Product.find(:all, :conditions => { :category_id => branch_ids } )
    end


end

并向控制器询问@category.all_products我收到以下错误:

Mysql::Error: Unknown column 'products.category_id' in 'where clause': SELECT `products`.* FROM `products` WHERE `products`.`category_id` IN (6, 8, 9)

我如何获得这个星座的所有产品?

更新2

好的,所以我要开始赏金。

如果我尝试:

def all_products    Categorization.find(:all,:conditions =&gt; {:category_id =&gt; branch_ids}) 端

我再次为{`获得undefined method命令' 我需要知道如何将many_to_many关系的所有产品作为ActiveRecord关系。

更新3

我把相关的代码放在一个要点中 https://gist.github.com/1211231

3 个答案:

答案 0 :(得分:5)

awesome_nested_set的关键是使用 lft列中的范围。 这是我如何使用直接关联(类别has_many文章)

的代码示例
  module Category
    extend ActiveSupport::Concern
    included do
      belongs_to :category
      scope :sorted, includes(:category).order("categories.lft, #{table_name}.position")
    end

    module ClassMethods
      def tree(category=nil) 
        return scoped unless category
        scoped.includes(:category).where([
          "categories.tree_id=1 AND categories.lft BETWEEN ? AND ?", 
          category.lft, category.rgt
        ])
      end
    end # ClassMethods
  end

然后在控制器中的某个地方

@category = Category.find_by_name("fruits")
@articles = Article.tree(@category) 

将找到苹果,橘子,香蕉等类别下的所有文章。 你应该通过加入分类来调整这个想法(但你确定你需要多对多的关系吗?)

无论如何我会尝试这个:

class Product < AR
      has_many :categorizations

      def self.tree(category=nil) 
        return scoped unless category
        select("distinct(products.id), products.*").
        joins(:categorizations => :category).where([
          "categories.lft BETWEEN ? AND ?", category.lft, category.rgt
        ])
      end
end

让我知道是否有任何问题

答案 1 :(得分:3)

有几种方法可以改进您的代码,但是如果您正在寻找属于某个类别的所有产品以及该类别的子代(后代),那么为什么您不会这样做:

def branch_ids
  self_and_descendants.map(&:id).uniq 
end

def all_products
  Product.find(:all, :conditions => { :category_id => branch_ids } )
end

我在这里做了一些假设,但我希望你明白这一点。

答案 2 :(得分:0)

扩展charlysisto的答案,当你处理has_and_belongs_to_many类别时,你很快就会发现蛮力的观点很慢......你需要某种“中间件”才能让它变得更快。 ..

socjopata的答案为如何改善这一点提供了一个很好的方法。所以,你使用定义

def branch_ids
  self_and_descendants.map(&:id).uniq 
end

在您的category.rb文件(型号)中。

然后,例如,在您的控制器中(带分页的示例):

@category = Category.find... # whatever
branches  = @category.branch_ids
@prodcats = ProductCategory.select('distinct product_id')
                           .where('category_id IN (?)',branches)
                           .order(:product_id)
                           .paginate(page: params[:page], :per_page => 25)
ids       = @prodcats.map(&:product_id)
@products = Product.where('id IN (?)', ids)

最后,在您看来,您需要一些“技巧”才能进行分页。您将在@prodcats上分页,而不是@products ...

<%= will_paginate @prodcats %>
<% @products.each do |product| %>
    ....
<% end %>

这种方法更快,在处理many_to_many 时,虽然不是100%在政治上正确,但我必须承认。