CRUD on attribute in N/N join table
Hi!
I am struggling with a many to many relation to access an attribute stored in the join table.
I basically have two models :
- "User" which has_or_belongs_to_many :items
- "Item" which has_or_belongs_to_many :users
In between, I have a join table "items_users" created via migration, where I added an extra attribute "quantity".
In order to CRUD these entities, what should be the process ? I came across this topic https://stackoverflow.com/questions/25235025/rails-4-accessing-join-table-attributes/25238779#25238779 but I couldn't make it work.
I came to Rails from a Laravel background and we use to do this for the problem exposed (it is just to show by code what I could mistakely explain by words):
- In a model Item, I declare my relationship as such: public function users() { return $this->belongsToMany(User::class)->withPivot('quantity') }
Which allows me to fetch all users that have the item like so (and access the property in the join table): $item->users
- In a model User, I declare my relationship as such:
public function items()
{
return $this->belongsToMany(Item::class)->withPivot('quantity')
}
Which allows me to fetch all items that have the user like so (and access the property in the join table): $user->items
To create this relation in DB, we execute:
$user->items()->attach($item->id, ['quantity' => 10]);
To access the property in a datatable for example, after a loop on $user->items, we display the quantity via $item->pivot->quantity
Hope I have been concise enough to explain the problem, I am junior on RoR, so I may misuse the terminology of things.
Regards.
has_and_belongs_to_many
does not allow you to access the join table, and therefore it's very rarely used.
Instead, you just want to define a has_many :through
association.
class User
has_many :user_items
has_many :items, through: :user_items
end
class UserItem
belongs_to :user
belongs_to :item
end
class Item
has_many :user_items
has_many :users, through: :user_items
end
This way you can interact with the join table and add things like quantity
.
Thanks a lot for your answer.
Is there any simplier way to access that attribute that what I am doing ? I am sure yes.
<% @user.items.each do |i| %>
<%= i.user_items.where(:user_id => @user.id).first.quantity %>
<%= i.name %>
<% end %>
Also, when it comes to create this relationship, how to write the create call ?
You should loop through @user.user_items.includes(:item)
instead so you can access the quantity without an extra query. The includes will load the items records efficiently so you aren't doing any N+1 queries.