Many-to-Many Relationships in Rails - ruby-on-rails

I have two models I want to connect with an m-to-m relationship, but I want the relationship to have some data of its own, such as a date due or a count or something like that...
Suppose I have Users, Groups, and some UsersInGroups object, where users and groups both have a has_many X, :through Y relationship. UsersInGroups belongs_to a user and a group, but also has a join_date that shows when a user joined the group.
so I can use self.groups.A to get Group variables from User and vice versa, but how do I get at the join_date variable?

In a many to many relationship, if a User can have many groups, and you do aUser.user_in_groups, it will return an array of the groups (which will be an instance of the model class representing them). You can iterate over each of these and obtain the join_date on each one, or by indexing into the array: aUser.user_in_groups[0].join_date
If you just want an array of join dates or something, I would look into the Ruby collect method.
Iteration:
aUser.users_in_groups.each do |group|
group.join_date
end

Related

set attributes in a has_many_though relationship in Rails

I have three models: Company, User and Employment. Each Company has many users though their Employments, and a user might belong to several companies through his employments.
Now, the tricky part: let's say my User1 belongs to 2 companies. He is SUPER_ADMIN in the first compnay, but just BASIC_USER in the second one. What would be the best way to define his roles ?
I used to have a simple has_many relationship between Company and User, which allowed me to simply set a is_admin attr on my User, but obviously this won't do with the new HMT relationship.
I thought of defining an array of IDs for each company, that would include the IDs of each admin user, but I'm pretty sure there is a cleaner way around.
In above scenario, you can use intermediate table i.e. Employment table to save all details of a user and it's associate company.
As Employment table will have ids of both user & company it will be easy for you to keep extra information related to user and company. Just add role column in this and use this to get information

Order by count of a model's association with a particular attribute

Say I have the two models Users and Appointments where a user has_many appointments.
An appointment can be of two different types: typeA and typeB.
How can I write a query to order the users by the amount of typeB appointments they have?
I've looked into counter_cache but it seems to just count the number of the association (so in this case the number of appointments a user would have) and does not allow for the counting of a particular type of appointment.
With joins (INNER JOIN) you'll get only those users, who have at least one appointment associated:
User.joins(:appointments)
.where(appointments: { type: 'typeB' })
.group('users.id')
.order('count(appointments.id) DESC')
If you use includes (LEFT OUTER JOIN) instead, you'll get a list of all users having those without appointments of 'typeB' at the end of the list.
Depending on the size and complexity of the database, it may be sometimes better to do two queries instead of joins on tables. In case you want to skip joins one way could be to get the order of Ids from one select query, and then retrieving the records from the second query.
ordered_user_ids = Appointments.select('user_id, count(1) AS N')
.where(:type => 'typeB').group(:user_id).order('N desc').collect(&:user_id)
# find users keeping the order of the users intact
User.find(ordered_user_ids, :order => "field(id, #{ordered_user_ids.join(',')})")

Multiple values for one column field in Rails

How do i create a column which stores multiple values for a User model in Rails app?
Example:
I want to have a User model and store multiple fruit preferences. What type of fruit_preference would i need to add in order to store multiple fruit_preference values? Such as: fruit_preference: apple, orange, pear
I want to find specific users based on one of those fruits in my app later on.
Answering the original question - array data type.
But what you really need is associations.
class User
has_many :fruits
end
class Fruit
belongs_to :user
end
Having such setup you will be able to query users to find those with specific fruit:
User.joins(:fruits).where(fruits: {name: 'apple'})
As well, as having all user's fruits (because he can have multiple):
User.first.fruits
#=> collection of Fruit objects
This is way better, that storing user's fruits as a collection in database, because pretty quick maintaining/changing/updating these collections becomes hard.

HMT or HABTM for Orders and Items

Fairly new to rails and trying to understand which relationships to use before going forward.
I have two models: orders and items. This is a many to many relationship, but I'm unsure of which relationship to use.
Orders might have delivery time, quantity of items, etc.
Lastly, what would you call the model joining orders and items if using HMT?
If you need to know anything else about the relationship of the item on a particular order, you need HMT.
If your items change price in the future, do you want to know how much they were sold for on orders in the past?
In this type of requirement, I've always had many "LineItem" records for an order, and the line_item instances belong_to to the item and order, and record the pricing and/or quantity for that order.
HMT vs HABTM? There are so few times that all you need is a many-to-many, that I'd almost always go with HMT for the extra ability to add more information to the association.
This seems like a classic case of HABTM, and the example given in the Rails Guides is perfect. The choice comes down to whether you need any other data or logic on the join model itself. If so, then use the HMT, where you will create a third active_record model to serve as the join table. You can name that anything you want. But it seems like HABTM will work for you, and all you need to setup is the join table with the default name (items_orders) in your migration, and rails will take care of everything else for you.
class Order < ActiveRecord::Base
has_and_belongs_to_many :items
end
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders
end

Simple Ruby on Rails database design

For practice I'm writing a shopping website where we have tables User and Item. A user obviously has_many items (when they are added to their basket), but the item, it belongs_to a User, even though many users will have the same item in their basket?
Furthermore, what if I want a list of items a user has added to their basket, but also a list of items they have viewed (for making suggestions based on searches), would it be better to have some 'through' tables: Basket and Viewed?
When you have this many-to-many relationships, you can use the HABTM schema:
Class User...
has_and_belongs_to_many :items
However, most of the time webshops use orderlines to keep up with items that users are purchasing. This means that an 'user' 'has_many' 'orderlines', an 'item' 'has_many' 'orderlines', an 'orderline' 'belongs_to' an 'user' and to an 'item'.
And maybe your orderlines will just be copies of items, and won't have a direct link because you don't want to alter the orderline after they have been processed. It really depends on the focus of your shop which scheme suits your needs.
Try to find some examples on the web and think about how you want to handle items, orders and baskets.
I'm used to separate things that are not the same, even if the relationship is one-to-one. So first of all I would recommend users from baskets (1:1-relationship).
After that a basket contains many items and items can be in multiple baskets (m:n-relationship). Make sure, that maybe a user likes to buy the same item multiple times.
views can be realised as a linking table between users and items: users have many views and items have many views, but one view is always linked to exactly one user and one item.

Resources