Allanon
Allanon

Reputation: 547

Rails find all products which belong to a collection of categories

So lets say that I have a Product and Category model. There is a one to many relationship between them ( category has many products, product belongs to category ). I create a collection of categories based on certain criterias using a named scope. Lets call this collection A. Then I would like to create a collection of products which belongs to either category in collection A.

How can I do this?

My guess would be to use the ids of collection A ( A.ids ) and use map or lambda to check if the category_id of a product is included in the array of ids I built from collection A. I am not sure how to do this exactly, and if is the most efficient way to achieve the end result.

Upvotes: 0

Views: 2621

Answers (5)

link_er
link_er

Reputation: 472

The simplest way is actually to take ids and use them to select products.

category_ids = Category.some_scope.pluck(:id)
products_collection = Product.where(category_id: category_ids)

Unless it is not always - that you need to select products from many categories - then maybe you should do that way with collection model, that suggested @dimakura

Upvotes: 1

D-side
D-side

Reputation: 9485

  1. Join on association, filter by accociated objects (joins/merge combo)

    Product.joins(:category).merge(Category.whatever_scope(you, have))
    
  2. Subquery on association

    Product.where(category: Category.whatever_scope(you, have))
    

Upvotes: 1

dimakura
dimakura

Reputation: 7655

I would suggest to arrange your models like this:

class Category
  has_many :products
  has_many :category_collections
end

class CategotyCollection
  belongs_to :category
  belongs_to :collection
end

class Collection
  has_many :category_collections
  has_many :categories, through: :category_collections
  has_many :products, through: :categories

end

In the end of the day, you can select products in given collection as simple:

collection = CategoryCollection.first
collection.categories # => all categories in this collection
collection.products # => all products in this collection

Upvotes: 1

Richard Peck
Richard Peck

Reputation: 76774

I'd be tempted to employ a has_many :through relationship, although it's really just a "join" model that's required:

enter image description here

I originally thought of a has_and_belongs_to_many relationship, but that wouldn't be able to discern the different categories.

--

#app/models/collection.rb
class Collection < ActiveRecord::Base
   #columns id | name | category_id | product_id
   belongs_to :category
   belongs_to :product
end

Having a Collection model like this will give you the ability to call the following:

@collection = Collection.find_by name: "New products"
@collection.categories #-> all categories
@collection.products #-> all products

This will give you the ability to relate the products and categories as follows:

#app/models/product.rb
class Product < ActiveRecord::Base
   has_many :categories
   has_many :collections
   has_many :collection_categories, through: :collections
end

#app/models/category.rb
class Category < ActiveRecord::Base
   has_many :products
   has_many :collections
   has_many :collection_products, through: :collections
end

Changing your Product and Category models is optional. It would be for if you wanted the following:

@category = Category.find "1"
@category.collection_products # -> pulled from collections (IE not the `has_many/belongs_to` relationship you have now.


@product = Product.find "2"
@product.collection_categories # -> all categories through the collections table

Upvotes: 1

David Meza
David Meza

Reputation: 3180

When you say that you "would like to create a collection of products which belongs to either category in collection A" that automatically makes it a many to many relationship. In reality one product can belong to many categories, and a category can have many products. Therefore, I would create a join table to solve your issue.

Upvotes: 0

Related Questions