model association for a e-store app - ruby-on-rails-3

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.

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.

Rails 3 has_many :through accessing attributes

I am working with a has_many through for the first time, and despite a lot of reading here and in the guide I am not understanding the correct way to access attributes on the through table. My tables are the same as this example from another post.
class Product < ActiveRecord::Base
has_many :collaborators
has_many :users, :through => :collaborators
end
class User < ActiveRecord::Base
has_many :collaborators
has_many :products, :through => :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :product
belongs_to :user
end
Assuming that the collaborators table has additional attributes, say hours_spent, what is the correct way to find the hours_spent from the collaborator table for a particular user and product?
When I have found my users via the product, and am iterating over them as in
#product.users.each do |user|
This seems to work
user.collaborator[0].hours_spent
I get the correct value, but since there should only be one collaborator record for each User/Product pair, the index is throwing me off, making me think I’m doing something wrong.
Thank you for reading!
EDIT
Perhaps I am not getting the has_many through concept. Maybe a MySQL example would help.
What I was thinking is that if I did
SELECT * FROM collaborators where user_id = 1;
I would expect a set (zero or more) as the result. Similarly
SELECT * FROM collaborators where product_id = 1;
would also give me a set, but
SELECT * FROM collaborators where user_id = 1 and product_id = 1;
would give at most 1 row.
If I am understanding properly, all 3 queries return a set. So I guess I need some kind of uniqueness constraint, but that would have to be a compound key of sorts, on both of the belongs to keys. Is that even possible? Is there a structure that better models this?
Thanks so much for the quick and helpful responses!
There may be a single database row per pair, but when considering a single user, that user can be associated to many products, so a user can have many rows in the collaborators table. Similarly, when considering a single product, that product can be associated to many users, so a product can have many rows in the collaborators table.
Also, instead of using user.collaborators[0].hours_spent, use user.collaborators.first.try(:hours_spent) (which may return null), if you only want the first collaborator's hours spent.
If a single user can only have one single product and a single product can only have a single user, then switch the has_many's to has_one's for everything.
Update: The preceding is the answer to the original question which has since been clarified via comments. See comments for detail and see comments on other answer by Peter.
Perhaps you should use has_and_belongs_to_many. If your Collaborator is used only to make link between User and Product without having more fields.
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
The beetween migration would be:
class CreateUsersProducts < ActiveRecord::Migration
def change
create_table "users_products", :id => false do |t|
t.integer :user_id
t.integer :product_id
end
end
end
After implementing this, what I found was that I think I had the correct relationships setup, I had to use the has_many :though as users could have many products, and it needed to be :through because there are additional attributes on the collaborator table. The sticking point was how to get there to only be a single Collaborator record for each user/product pair, and then how do I guarantee I got it. And to this point the answer I've found is it has to be done in code.
To make sure there is only a single record for each pair, I used
class Collaborator < ActiveRecord::Base
validates :product_id, :presence => true, :uniqueness => {:scope => [:user_id], :message => "This is a duplicate join"}
And then to make doubly sure I'm finding the right record, I have a scope
scope :collaboration_instance, lambda {|p_id, u_id| where("collaborations.product_id = ? && collaborations.user_id = ?", p_id, u_id)}
If someone has a more elegant solution, or just wants to improve this one, please post and I will change yours to the selected answer.

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

Rails - many associations in one view

Thanks to stackoverflow users, stackoverflow helped me alot for my project until now. Now I face a new problem.
I already implemented this project http://img7.imagebanana.com/img/cnb46ti2/relationships.png (see Rails - data view from has_many and Has_many_and_belongs_to_many). For connecting positions and skills, I made an has_and_belongs_to_many relationship, because every position has many skills and every skills has many positions and for this table I didn't want to add some columns. The table 'positions_skills' puts both together.
Now I want to add 2 new tables.
current_qualification http://img7.imagebanana.com/img/bimfgvut/UebersichtQualifikationenbistqualifi.png
expected_qualification http://img7.imagebanana.com/img/72p0yj27/UebersichtQualifikationencsollqualif.png
if it's not clear what these tables with the qualification stars do: Every skill in a position has it's own level of qualification (for example for Position 1, you need to be competent in skill 1 alot (5 stars), but for Position 2, you need not so much knowledge about this skill(2 stars)). -> expected_qualification. And every employee is different good in each skill -> current_qualification
Question 1: The table 'expected_qualification' seems similar to the table 'positions_skills'. Am I right that if I add the table 'expected_qualification', than 'position_skills' is not necessary anymore? I just used 'position_skills' for connecting this n:m relationship for viewing employee's skills on show.html.erb, but it's also possible with 'has_many :through' associations if I understood right.
Question 2: Both tables are necessary for viewing these little stars on the view of employee's show.html.erb. (as you can see on my pictures. But right now I'm satisfied with numbers instead of Stars => 4 stars = 4 as integer in column). What did I do?
I created the table 'current_qualification' with rails generate scaffold current_qualification employee_id:integer skill_id:integer qualificationstars:integer
I added code into the models for has_many :through association
I filled the table for current_qualification
and now the tricky part: The employee controller! Because I already have an has_and_belongs_to_many association, the controller looks like this: def show #employee = Employee.find(params[:id], :include => { :position => :skills }) end .... How do I add :include => { :employee => { :current_qualifications => :skills} } for the has_many :through relation now into the already existing code? And which code lets me output the qualificationstars-column from the current_qualification-table for show.html.erb of employee?
Thanks in advance! And sorry for so much questions.

rails: filter categories where there are at least one article

I have articles and categories in a n:m relation:
I looking for a find statement on the Category Model so that I can get all categories witch consist at least one article.
Should be easy, but I didn't find a efficient solution, without searching retrieving all the articles.
Thanks,
Maechi
I think that counter cache is your friend here. Take a look here.
You can add the counter cache to the categories table and in the CategoryArticles you do like
class CategoryArticles
belongs_to :article
belongs_to :category, :counter_cache => true
end
So you can find your Category with
#categories = Category.find(:all, :conditions => ["category_articles_count > ?", 0])