Scope nested model with ancestry gem - ruby-on-rails-3

I use two models:
Productgroup
has_ancestry
has_many :products
Product
belongs_to :productgroup
The product model is using the attribute "active".
Now I would like to scope the Productgroup model to get only productgroups with active products. There may be root productgroups with no products but with childs that have active products. So I'm looking for something like this:
#productgroups = Productgroup.includes(:products).where("productgroups.children.products.active IS ?", true)
Does anyone has an idea how to do this?

Related

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.
I'd be tempted to employ a has_many :through relationship, although it's really just a "join" model that's required:
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
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
Join on association, filter by accociated objects (joins/merge combo)
Product.joins(:category).merge(Category.whatever_scope(you, have))
Subquery on association
Product.where(category: Category.whatever_scope(you, have))
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
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.

How to add multiple models to cart Rails

Can you please help how to add items to cart. Rails Agile explains how to add products to carts through line_items.
Let's say my websites offers tourist packages, limo services, apartment rooms,some other deals.
Customer adds limo car, tour package, offers to cart and pays for it.
Are they different models model Package, model Limo, model Apartment and other models. if so when I add to cart through line_items(cart_id, product_id) I can't figure out how to add other models. Or Should I link all models to product model?
Thanks in advance
You probably don't need a different model for every product type. You can break down the products into different categories. So you would need just one extra model--a Category model. Each product would belong_to a category (and category would have many products). In the database each product would have a category_id.
Like this:
class Product < ActiveRecord::Base
belongs_to :category
....
class Category < ActiveRecord::Base
has_many :products
...
You mention that models are too different (in terms of attributes). Therefore single-table-inheritance may not be what you want. In this case use polymorphic association to achieve this.
class LineItem < ActiveRecord::Base
belongs_to :purchasable, :polymorphic => true
end
class Tour < ActiveRecord::Base
has_many :line_items, :as => :purchasable
end
class LimoService < ActiveRecord::Base
has_many :line_items, :as => :purchasable
end

model association for a e-store app

I'm developing an e-store app using ruby on rails and I'm a bit confused about the model associations. It would help me if someone could give me an idea about the tables and their associations. Here are the details:
Parent tables:
=>Categories,
occasions,
Hot Deals
Under Categories:
=>Men,
Women,
Kids
Under Men:
=>Shirts,
Trousers
Under Women:
=>Shirts,
Trousers,
Skirts
Under Kids:
=>Shirts,
Trousers
Under Occasions
=>Ethnic,
Party,
Travel,
Casual,
Formal
Under Hot Deals
=>Hot Deals Index
And lastly every last sub-table will have product index.
Thank you!
What you would generally do with something like this is build a Taxonomy tree to that would be associated with the products, allowing you to group products together. A many to many relation between Taxonomy and Product let's you associate a product with multiple groups, so a T-Shirt might be under Categories > Men > Shirts as well as Occasion > Casual.
# app/models/taxonomy.rb
class Taxonomy < ActiveRecord::Base
has_many :taxonomies
has_and_belongs_to_many :products
end
# app/models/product.rb
class Product < ActiveRecord::Base
has_and_belongs_to_many :taxonomies
end
then a migration for the join table between taxonomy/product
rails g migration create_products_taxonomies
and edit it
def change
create_table(:products_taxonomies, :id => false) do |t|
t.references :product
t.references :taxonomy
end
end
From there you would basically create in the database 3 Taxons, 1 for each of your sections, then create the Taxonomy and build out the sub levels. When you create your products, assign the right Taxonomy to the product and your set.
A seed file might look like...
Taxonomy.create!(:name => "Category").tap do |category|
category.taxonomies.create!(:name => "Men").tap do |men|
men.taxonomies.create!(:name => "Shirt")
men.taxonomies.create!(:name => "Trousers")
end
# and so on for each category
end
Then when you create a Product, you can associate it with the Taxonomy and use that Taxonomy to pull up a list of products that are associated with it.

Track sum of some fields in the association - "sum_cache"

I have tables 'orders' and 'items' with has_many association in the model.
class Order < ActiveRecord::Base
has_many :items
class Item < ActiveRecord::Base
belongs_to :order
Item consists of 'quantity' field, and Order consists of 'quantity_sum' field to track sum of associated items quantity.
For eg:
Order 1 : name='Toms shopping cart', quantity_sum=12
Item 1 : name='T-shirt', quantity=10
Itme 2 : name='Shoes', quantity=2
I have been looking for a way so that whenever new item is added/edited/deleted, the field 'quantity_sum' of Order gets updated automatically. Presently I have been using after_save method in Item, to update 'quantity_sum' field of Order.
Is there any other neat way of doing this besides 'after_save' ???
Similar to "counter_cache" for tracking count of associations, does rails have support for automatically keeping track of sum of some fields in the association?
Thanks
Remove the quantity_sum field from your table and add a quantity_sum method to the order class that sums up the quantity_values
class Order < ActiveRecord::Base
has_many :items
def quantity_sum
self.items.sum(:quantity)
end
end
Should do the trick. All you then need to do is remove any code you may have that updates the quantity_sum field. You will find that because the name of the method is the same as the field name (That you must not forget to delete) you won't have to refactor any of your code that makes use of it.
Obviously you need to be careful not to use this field unneccesarily like in a list of all orders in the system as this will be quite heavy on the database. O.K for a few hundred records but you'll notice a performance issue over thousands of orders.
Don't forget to remove that quantity_sum field from the order table
I think that this gem is what your'e looking for.
Look under "Totaling instead of counting" in the docs.
It should allow you to to something like this:
class Item < ActiveRecord::Base
belongs_to :order
counter_culture :order, :column_name => 'quantity_sum', :delta_column => 'quantity'
end
class Order < ActiveRecord::Base
has_many :items
end

rails and namespaced models issue

Using rails 3/3.1 I want to store invoices with their items (and later more associations like payments, etc…).
So in a first approach I set up the models like this:
class Invoice < ActiveRecord::Base
has_many :invoice_items
end
class InvoiceItem < ActiveRecord::Base
belongs_to :invoice
end
And the routes likes this:
resources :invoices do
resources :invoice_items
end
I chose InvoiceItem instead of Item because I already have a model named Item and I somehow want to namespace the model to invoices. But this name has the huge disadvantage that one has to use invoice.invoice_items instead of a intuitive invoice.items. Also the generated url helpers look real ugly, for example "new_invoice_invoice_item_path(invoice)" (notice the double invoice_invoice).
So I changed to namespaced models like this:
class Invoice < ActiveRecord::Base
has_many :items, :class_name => "Invoice::Item"
end
class Invoice::Item < ActiveRecord::Base
belongs_to :invoice
end
And the routes likes this:
resources :invoices do
resources :items, :module => "invoice"
end
Now the assocation is named nicely and also the url helpers look pretty. But I can't use dynamic urls (ex. [:new, invoice, :item]) anymore, because the controller is set to "invoice_item" instead of "invoice/item".
I wonder how other people solve this problem and what I'm doing wrong. Or is this simply a bug in rails 3.0.7/ 3.1.rc?
EDIT:
Sorry, I seems I didn't correctly express my concern. My model Item is not related to Invoice::Item. Order::Item is also not related to Item nor Invoice::Item. An Invoice::Item can only belong to one invoice. An Order::Item can only belong to an Order. I need to namespace - but why doesn't rails properly support namespacing out of the box? Or what am I doing wrong with namespacing?
Corin
If an order item and an invoice item are not the same object in the real world then I would name them differently rather than trying to namespace, for example OrderItem and InvoiceItem - this will keep things clearer as your codebase grows and avoid the need to make sure you use the right namespace everywhere you reference an Item.