Today I want to talk about a feature of Laravel which is really useful but can be potentially difficult to understand at first. Pivot table is an example of intermediate table with relationships between two other "main" tables.
Real-life example of pivot tables
In official documentation they show the example of User-Role relationships, where user potentially can belong to several roles, and vice versa. So to make things clearer - let's take another real-life example: Shops and Products. Let's say a company has a dozen of Shops all over city/country and a variety of products, and they want to store the information about which Product is sold in which Shop. It's a perfect example of many-to-many relationship: one product can belong to several shops, and one shop can have multiple products.So here's a potential database structure:
shops- - id
- - name
- - id
- - name
- - product_id
- - shop_id
- Name of the pivot table should consist of singular names of both tables, separated by undescore symbol and these names should be arranged in alphabetical order, so we have to have product_shop, not shop_product.
- To create a pivot table we can create a simple migration with artisan make:migration or use Jeffrey Way's package Laravel 5 Generators Extended where we have a command artisan make:migration:pivot.
- Pivot table fields: by default, there should be only two fields - foreign key to each of the tables, in our case product_id and shop_id. You can add more fields if you want, then you need to add them to relationship assignment - we will discuss that later.
Models for Many-to-Many Relationships: BelongsToMany
Ok, we have DB tables and migrations, now let's create models for them. The main part here is to assign a many-to-many relationship - it can be done from either of "main" tables models. So, option 1: app/Models/Shop.php:class Shop extends Model { /** * The products that belong to the shop. */ public function products() { return $this->belongsToMany(Product::class); } }
class Product extends Model { /** * The shops that belong to the product. */ public function shops() { return $this->belongsToMany(Shop::class); } }
public function products() { return $this->belongsToMany(Product::class, 'products_shops'); }
public function products() { return $this->belongsToMany(Product::class, 'products_shops', 'shops_id', 'products_id'); }
Managing Many-to-Many Relationships: attach-detach-sync
So, we have tables, and we have Models ready. Now, how do we actually save the data with a help of our two Models instead of the third intermediate one? Couple of things here. For example, if we want to add another product to the current shop instance, we use relationship function and then method attach():$shop = Shop::find($shop_id); $shop->products()->attach($product_id);
$shop->products()->detach($product_id);
$shop->products()->detach();
$shop->products()->attach([123, 456, 789]); $shop->products()->detach([321, 654, 987]);
$product->shops()->sync([1, 2, 3]);
Additional Columns in Pivot Tables
As I mentioned above, it's pretty likely that you would want more fields in that pivot tables. In our example it would make sense to save the amount of products, price in that particular shop and timestamps. We can add the fields through migration files, as usual, but for proper usage in relationships we have to make some additional changes to Models:public function products() { return $this->belongsToMany(Product::class) ->withPivot('products_amount', 'price') ->withTimestamps(); }
foreach ($shop->products as $product) { echo $product->pivot->price; }
$shop->products()->attach(1, ['products_amount' => 100, 'price' => 49.99]);
Hi, Povilas, Any plans to update "Eloquent Relationships: The Ultimate Guide" on @QuickAdmin or Premium Tutorials in text version on @DailyLaravel? I tend to get stuck on Eloquent Relationships! I tweeted it as well if you missed that one. π
Replied on Twitter :)
Great tutorial - I love the Generators package! One question - as the pivot table doesn't require a seperate model for the intermediate table, how would we use a factory to populate this with data?
e.g I have three tables
courses
,students
&course_student
.Course
&Student
has a model so e.g I can useStudent::factory(100)->create();
in my DatabaseSeeder class to create 100 rows in the students table, however I would like to be able to use a factory to populate data in thecourse_student
pivot table, is this possible using factories or should I just populate the data manually?Used this method to do some form of populating data in case it helps anyone else https://laravel.com/docs/9.x/eloquent-relationships#one-to-one
Yes, it is possible in factories, something like
Course::factory(10)->create()->each(function(Course $course) { $course->students()->attach(1); });
brilliant very simple and efficient thanks Povilas
Is it necessary to create a model file for pivot tables?
Thanks for this article its very clear.
Hi,
Thanks for this article. I do have a question on how we can use pivot table with extra fields in filamentphp. Exectly like the one you implenmented in https://blog.quickadminpanel.com/laravel-belongstomany-add-extra-fields-to-pivot-table/.
Is there any ready to use plugin/field available in the filament or how we can achieve this ?
This should help you:
https://laraveldaily.com/post/filament-belongstomany-select-automatically-fill-pivot-values
Hi Modestas,
Thank you for the article link. But my requriement is like exacly like in that article. i want to give option to select ( using checkboxes using checkbox list component of filament and ask to select a date using date picker upon selecting a checkbox item )
Sorry, I didn't quite understood the problem here...