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

发布于 2024-12-03 08:49:50 字数 2409 浏览 1 评论 0原文

正在开发一个电子商务应用程序,我试图解决以下问题: 我通过 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 => { :category_id =>branch_ids } ) 我再次

得到 undefined methodorder' for #` 我需要知道如何将 Many_to_many 关系的所有产品作为 ActiveRecord 关系获取。

更新3

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

having an e-commerce application under development i am trying to get my head around the following problem:
I have my categories realized through the awesome_nested_set plugin.
If I list my articles through selecting one category everything works fine, but for some links I want to show all the products of one category and the products of its child categories.

here is the controller code that works fine with only one category:

   # 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

The line I commented out is a helper method I wrote myself in the category model which returns all products of the category and its child categories in an array.

It is defined as followed:

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

Now when I uncomment this line and try to select one category my products controller code breaks with errors like

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

or

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

because I am using ordering and pagination and it can't order the arary anymore.

Any ideas how to get all the products in an ActiveRecord Relation element in my controller?
thanks

UPDATE

so, when I use the following:

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

and ask the controller for @category.all_products i get the following error:

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

How would I get all products with this constellation?

UPDATE 2

Ok, so I am going to start a bounty.

If I try:

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

I get again undefined methodorder' for #`
I need to know how I can get all the products of a many_to_many relation as an ActiveRecord relation.

UPDATE 3

I put the relevant code in a gist
https://gist.github.com/1211231

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

唐婉 2024-12-10 08:49:50

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

如果有任何问题,请告诉我

The key with awesome_nested_set is to use a range in the lft column.
Here's a code sample of how I do it with a direct association (category has_many articles)

  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

Then somewhere in a controller

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

that will find all articles under the categories apples, oranges, bananas, etc etc.
You should adapt that idea with a join on categorizations (but are you sure you need a many to many relationship here?)

Anyway I would try this :

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

Let me know if there's any gotcha

天荒地未老 2024-12-10 08:49:50

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

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

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

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

There are a several ways you could improve your code, but if you're looking for all products that belong to a category and children (descendants) of the category then why you won't do it like this:

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

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

I've done some assumptions here, but I hope you get the idea.

谁对谁错谁最难过 2024-12-10 08:49:50

扩展 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%政治正确,但我必须承认。

Extending charlysisto 's answer, when you are dealing with has_and_belongs_to_many categories, you will soon find out that the brute force perspective is quite slow... You need some kind of "middleware" to make it faster...

socjopata 's answer gives a good approach of how to improve this. So, you use the definition

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

in your category.rb file (model).

Then, for example, in your controller (example with pagination):

@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)

Finally, in your view, you will need a little "technique" in order to have pagination. You will paginate on @prodcats, not @products...

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

This approach is way faster, WHEN DEALING WITH many_to_many, though not 100% politically correct, I must admit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文