Display products of similar category RoR

Go To StackoverFlow.com


I have Four models.

Product Category Categorization Images


has_many  :images 
has_many  :categorizations
has_many  :categories, :through => :categorizations


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


belongs_to :category
belongs_to :product


belongs_to :product

When a user clicks to see a product, I want to have a section on the bottom that shows images similar products (based on same category/categories).

I guess my problem is that there are so many nested relationships that I don't know how to extract the relationships out.

Any help is appreciated. Thanks.

2012-04-03 19:40
by noob


Just think of what object you are currently working with and what object you need to access. In this case I assume you set up @product in the controller, and it seems that you want a collection @similar_products that you then loop through on the bottom of the page.

We have a product to work with at the beginning. From this, we want products that are in the same categories as the product. So, in your controller do

@similar_products = @product.categories.inject({}) do |result_hash, category|
    result_hash[category.name.to_sym] => category.products.reject {|p| p == @product}

So what we end up with is something that looks like this:

{:category_1 => [product1, product2, product3], :category_2 => [product4,product5]...}

I might recommend limiting the number of products for each category to something like 5 by changing the result_hash assignment to

result_hash[category.name.to_sym] => category.products[0..4]

Now in your view you can loop through the products by category if you like:

<% @similar_products.each do |k,product_array| # remember that the key is the name and the value is an array %>
    <% product_array.each do |product| %>
        <img src="<%= product.image.path %>" />
    <% end %>
<% end %>
2012-04-03 20:56
by JamesSwift
There's with_object instead of inject to avoid ugly result_hash constructions - Reactormonk 2012-04-03 21:03
Thanks for helping. I have several questions - noob 2012-04-04 15:44
The first is that I changed the result_hash[category.name.to_sym] => category.products.reject {|p| p == @product} to result_hash[category.name.to_sym] = category.productss.order('rand()').limit(4).reject { |p| p == @product}. But the issue is that even though I limit it to 4 items, it continues to show more items at times when I refresh the page - noob 2012-04-04 15:54
Second question is, if i have product two that belongs to the same two categories as product 1, how do I prevent product 2 from showing up twice in the similar products? I tried adding uniq! to the end of this result_hash[category.name.to_sym] = category.productss.order('rand()').limit(4).reject { |p| p == @product} but it returned an error - noob 2012-04-04 15:54
I'm not sure about question 1, but for question 2 it looks like you are applying uniq! at the wrong place. You need to apply it to the whole hash with something after the whole inject like @similar_products = @similar_products.to_a.flatten.uniq which will leave you with an array like [:cat_one,product1,product2,:cat_two,product3,product4] - JamesSwift 2012-04-04 21:19
@Tass would you mind showing an example of using that here, I had never used it. When I looked it up, it appears that it is a different inject syntax, is that correct - JamesSwift 2012-04-04 21:21
enum.with_object({}) {|(*args), hash| hash[key] = value }Reactormonk 2012-04-05 08:33