Many to many relationships use the belongsToMany paradigm and can be a little tricky at first as they are a bit more advanced than the hasMany and belongsTo relationships, but once you spend some time digging into them, they are not as bad as they first seem. Many to Many relationships are everywhere out in the wild. They can be implemented by hand or in a variety of different languages and frameworks. Laravel has a great implementation of the many to many relationship pattern, so let’s check it out now!

Think of the Real World

It’s easiest to think of real world scenarios when considering many to many relationships. You are most certainly familiar with wordpress the popular blogging platform. When you create blogposts in wordpress, you assign tags to the posts to help categorize them. Now it is entirely possible, and likely, that each blog post will have many tags. By the same token, a tag may be found assigned to more than one blog posts. Maybe you write about the stock market, and if so there may be many posts that deal with earnings reports. Perhaps there are 10 posts which deal with earnings. You should be able to query on a given tag and bring back all of the posts related to that tag. Additionally, you should be able to query the blogpost, and find out what tag or tags are associated with it. We call this the many to many relationship.

We could ask the question, “Does a blogpost have many tags?” The answer would likely be Yes. We could also ask, “Does this Tag have many Blogposts?” Again, the answer is most likely in the affirmative 🙂 Therefore, we are dealing with many to many relationships.

Set up your Database First

In this example we are going to be dealing with Blog Posts and Tags. Let’s create some tables to help us with this tutorial. First up is the blogposts table:

php artisan generate:migration create_blogposts_table --fields="title:string, body:text"

Excellent! Laravel’s Artisan tool to the rescue to save us some time.

Next up we need a tags table and Artisan is going to make that very easy as well:

php artisan generate:migration create_tags_table --fields="name:string"

When we generate the migration, we are creating php files on the filesystem. Once we do this, we need to actually run the migration like so:

php artisan migrate

Awesome! We have our two tables to help us with studying many to many relationships. What we do not have yet is a link between the two. The best way to do this is by way of a pivot table. Using the great Jeffrey Way’s Generator tool, we will do this right now:

php artisan generate:pivot blogposts tags

We need to run the migration again:

php artisan migrate

Great! By giving the php artisan generate:pivot command the two tables we’re dealing with, blogposts , and tags , the tool will create a blogpost_tag table to help us with tracking our relationships.

Set Up Your Models

We don’t have any Models yet, so lets whip some up right now:

Create the Blogpost Model

php artisan generate:model Blogpost

Remember that we create Models based on the singular form of the database table we are dealing with. So our blogposts table has a Blogpost model and our tags table has a Tag model. Check.

Create the Tag model

php artisan generate:model Tag

Ok, we’ve created a lot just by using artisan. Let’s open up our files and fine tune them to support many to many relationships!

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Blogpost extends Eloquent { protected $guarded = array ( ) ; public function tags ( ) { return $this -> belongsToMany ( 'Tag' ) ; } } class Tag extends Eloquent { protected $guarded = array ( ) ; public function blogposts ( ) { return $this -> belongsToMany ( 'Blogpost' ) ; } }

Cool! We can use the same trick we used in our Laravel Eloquent Tutorial. If you recall we can Start with the $this keyword, followed by the class name of the file, followed by the $this method, followed by the Model passed in. So in these two cases we’ll have This Blogpost belongsToMany Tag and This Tag belongsToMany Blogpost. I love this stuff 🙂

For brevity, we have already seeded the database with some blogposts, tags, and also relationships in our blogpost_tag pivot table. Feel free to add some blogposts and tags to your database if you are following along so you can test it for yourself.

We’re going to focus on retrieving data to start. Right now, our tags table and blogpost_tag pivot table look like this:

mysql> select * from tags;

+----+-----------+---------------------+---------------------+ | id | name | created_at | updated_at | +----+-----------+---------------------+---------------------+ | 1 | laravel | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | | 2 | bootstrap | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | | 3 | windows | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | +----+-----------+---------------------+---------------------+

mysql> select * from blogpost_tag;

+----+-------------+--------+ | id | blogpost_id | tag_id | +----+-------------+--------+ | 1 | 1 | 2 | | 2 | 2 | 2 | | 3 | 3 | 1 | | 4 | 4 | 1 | | 5 | 4 | 3 | | 6 | 5 | 3 | +----+-------------+--------+

This is pretty cool. You can see we have 3 tags in our tags table, namely laravel, bootstrap, and windows. In our blogpost_id pivot table we have the relationships that exist between those tags, and the blogposts in the database. Going line by line we can see that:

The blogpost with id of 1 has a tag id of 2

The blogpost with id of 2 also has tag id of 2

The blogpost with id of 3 has a tag id of 1

The blogpost with id of 4 has a tag id of 1

The blogpost with id of 4 also has a tag id of 3

has a tag id of 3 The blogpost with id of 5 has a tag id of 3

Use Laravel to Interact with the Database

Let’s get our blogposts by way of the Tag model. Basically we are going to ask the database to Give us all blogposts that have a tag_id of 1 (laravel) associated with it:

1 2 3 4 5 Route:: get ( '/' , function ( ) { $tag = Tag:: find ( 1 ) ; return $tag -> blogposts ; } ) ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [ { "id" : 3 , "title" : "Laravel Eloquent ORM Tutorial" , "body" : "Eloquent is the very powerful and expressive ORM or Object Relational Mapper in Laravel. If you know how to work with Objects in PHP, then you know how to use Eloquent! " , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "tag_id" : 1 , "blogpost_id" : 3 } } , { "id" : 4 , "title" : "Install Laravel on Windows" , "body" : "This is going to assume we are starting from scratch using a fresh install of WAMP 2.4 on Windows." , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "tag_id" : 1 , "blogpost_id" : 4 } } ]

Now we’ll do the same for tag_id 2 (bootstrap) and tag_id 3 (windows)

1 2 3 4 5 Route:: get ( '/' , function ( ) { $tag = Tag:: find ( 2 ) ; return $tag -> blogposts ; } ) ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [ { "id" : 1 , "title" : "You Should Use Twitter Bootstrap!" , "body" : "If you are a design savvy person, you may have noticed that Vegibit uses the Twitter Bootstrap Framework. I freaking love this framework, and you should too, or rather if you’re unfamiliar, you will love it by the time you finish reading this article." , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "tag_id" : 2 , "blogpost_id" : 1 } } , { "id" : 2 , "title" : "Twitter Bootstrap Modal Tutorial" , "body" : "Modals are a fun way to add interactivity to your website. When a user needs to make a choice, or confirm an action, the classic jquery modal popup makes perfect sense." , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "tag_id" : 2 , "blogpost_id" : 2 } } ]

1 2 3 4 5 Route:: get ( '/' , function ( ) { $tag = Tag:: find ( 3 ) ; return $tag -> blogposts ; } ) ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [ { "id" : 4 , "title" : "Install Laravel on Windows" , "body" : "This is going to assume we are starting from scratch using a fresh install of WAMP 2.4 on Windows." , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "tag_id" : 3 , "blogpost_id" : 4 } } , { "id" : 5 , "title" : "Install and Configure Windows Server 2012 with Windows Powershell" , "body" : "Windows PowerShell is an amazingly powerful tool to help you with common windows administrative tasks. If you are comfortable with one of the myriad of scripting languages available today, Powershell will be easy for you to learn while also providing the benefits of command line administration. " , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "tag_id" : 3 , "blogpost_id" : 5 } } ]

We can also now do this in reverse! Let’s tell the database to Give us all tags that have a blogpost_id of 4

1 2 3 4 5 Route:: get ( '/' , function ( ) { $blogpost = Blogpost:: find ( 4 ) ; return $blogpost -> tags ; } ) ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ { "id" : 1 , "name" : "laravel" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 1 } } , { "id" : 3 , "name" : "windows" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 3 } } ]

Here we can see that Laravel correctly brings back tags of laravel and windows, since they are both associated with the blogpost_id of 4 (Install Laravel on Windows).

Inserting Related Models

We’ve done a lot of retrieving of data, what about if we want to assign a new tag to a particular blogpost? How do we do this? Let’s say our blogpost Install Laravel on Windows could benefit from an additional tag. Let’s assign an additional tag of wamp to this post. Check it out:

1 2 3 4 5 6 7 Route:: get ( '/' , function ( ) { $tag = new Tag ( array ( 'name' = > 'wamp' ) ) ; Blogpost:: find ( 4 ) -> tags ( ) -> save ( $tag ) ; return $blogpost -> tags ; } ) ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 [ { "id" : 1 , "name" : "laravel" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 1 } } , { "id" : 3 , "name" : "windows" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 3 } } , { "id" : 4 , "name" : "wamp" , "created_at" : "2014-02-04 17:32:36" , "updated_at" : "2014-02-04 17:32:36" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 4 } } ]

Wow! This is why people are going bananas over Laravel. Just read the syntax, it makes so much sense! Create a new tag, find the blogpost we want to assign it to, and save it. Done.

Now, let’s say you refreshed the page one too many times and it creates the wamp tag twice. (Yes, I did this 🙂 ) We don’t need the same tag assigned to a blogpost twice, so let’s remove one of them with the detach method.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 [ { "id" : 1 , "name" : "laravel" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 1 } } , { "id" : 3 , "name" : "windows" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 3 } } , { "id" : 4 , "name" : "wamp" , "created_at" : "2014-02-04 17:32:36" , "updated_at" : "2014-02-04 17:32:36" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 4 } } , { "id" : 5 , "name" : "wamp" , "created_at" : "2014-02-04 17:32:54" , "updated_at" : "2014-02-04 17:32:54" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 5 } } ]

1 2 3 4 5 6 7 Route:: get ( '/' , function ( ) { $blogpost = Blogpost:: find ( 4 ) ; $blogpost -> tags ( ) -> detach ( 5 ) ; return $blogpost -> tags ; } ) ;

And just like that, we have removed the second instance of wamp from Install Laravel on Windows.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 [ { "id" : 1 , "name" : "laravel" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 1 } } , { "id" : 3 , "name" : "windows" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 3 } } , { "id" : 4 , "name" : "wamp" , "created_at" : "2014-02-04 17:32:36" , "updated_at" : "2014-02-04 17:32:36" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 4 } } ]

We can also use the attach method to assign a tag to a blogpost like this:

1 2 3 4 5 6 7 Route:: get ( '/' , function ( ) { $blogpost = Blogpost:: find ( 4 ) ; $blogpost -> tags ( ) -> attach ( 2 ) ; return $blogpost -> tags ; } ) ;

Well will you take a look at that 🙂 Look at our bootstrap tag, now happily attached to this blogpost.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 [ { "id" : 1 , "name" : "laravel" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 1 } } , { "id" : 2 , "name" : "bootstrap" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 2 } } , { "id" : 3 , "name" : "windows" , "created_at" : "0000-00-00 00:00:00" , "updated_at" : "0000-00-00 00:00:00" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 3 } } , { "id" : 4 , "name" : "wamp" , "created_at" : "2014-02-04 17:32:36" , "updated_at" : "2014-02-04 17:32:36" , "pivot" : { "blogpost_id" : 4 , "tag_id" : 4 } } ]

Using the Sync Method

The sync method is also a very useful tool to assign related models in many to many relationships. This is an explicit command, meaning you hand it a list of id’s by way of an array, and those values are assigned to the pivot table while overwriting any existing values. Let’s seen an example. First, we’ll assign all tags we currently have to a blog post, then we’ll assign just one tag to the same post, both times using the sync method. You will see that whatever the sync method gets for arguments, is the state the pivot table will have once complete: