Many To Many Relationships With Laravel
Part 21 of 48 in API Driven Development With Laravel and VueJSSo in this tutorial, we will go through one of the more useful, but advanced relationships you can do with Laraval, the many-to-many relationship between models with eloquent. Eloquent, Laravel’s ORM makes database connections and querying a breeze. We will be using the many-to-many relationship to add brew methods to cafes.
The key with many-to-many relationships is the join (or pivot) table. This allows the relationship id from one model to be related to many other models and vice-versa. This makes querying simple for questions like “Which cafes have this brew method?” and “Which brew method does this cafe have?”
Step 1: Create Brew Methods Table
We will need a simple table created for our brew methods. To do this we need to run a migration.
First we will need to open the terminal on the server and run:
php artisan make:migration added_brew_methods
This will create our migration. find the migration in the /app/database/migrations
directory and open it up.
The table will be really simple, we will need an ID, timestamps, and a brew method name. In your migration up method, add the following code:
Schema::create('brew_methods', function( $table ){
$table->increments('id');
$table->string('method');
$table->timestamps();
});
We now have an auto incremented id for the brew method and a method which will store names like ‘Aeropress’, ‘V-60 Pour Over’, etc.
Just to clean up the migration, make sure you have:
Schema::dropTable('brew_methods');
In the down method.
Now run php artisan migrate
and you will have a brew methods table.
I seeded the table with the following Insert
query:
INSERT INTO `brew_methods` (`id`, `method`, `created_at`, `updated_at`)
VALUES
(1, 'Hario V60 Dripper', '2017-10-29 01:41:52', '2017-10-29 01:41:52'),
(2, 'Chemex', '2017-10-29 01:41:59', '2017-10-29 01:41:59'),
(3, 'Siphon', '2017-10-29 01:42:25', '2017-10-29 01:42:25'),
(4, 'Kyoto Cold Brew', '2017-10-29 01:42:45', '2017-10-29 01:42:45'),
(5, 'Clover', '2017-10-29 01:42:52', '2017-10-29 01:42:52'),
(6, 'Espresso', '2017-10-29 01:43:18', '2017-10-29 01:43:18'),
(7, 'Aeropress', '2017-10-29 01:43:41', '2017-10-29 01:43:41'),
(8, 'French Press', '2017-10-29 01:44:02', '2017-10-29 01:44:02'),
(9, 'Kalita Wave Dripper', '2017-10-29 01:44:58', '2017-10-29 01:44:58'),
(10, 'Nitrous', '2017-10-29 01:45:35', '2017-10-29 01:45:35');
These should be managed by some sort of admin backend (yes this is foreshadowing for future tutorials) but for now this will work!
Step 2: Create Join Table
The join table is where the magic happens for many-to-many relationships. This table simply links the IDs of the two joining models, in this case ‘Brew Methods’ and ‘Cafes’. The table is simple and contains the columns for both cafe_id
and brew_method_id
which are set up as foreign keys as best practice for database structure.
First we will make another migration so run:
php artisan make:migration added_cafes_brew_methods
Open the new file in your app/database/migrations
. You will need to add this code to the create()
method:
Schema::create('cafes_brew_methods', function( $table ){
$table->integer('cafe_id')->unsigned();
$table->foreign('cafe_id')->references('id')->on('cafes');
$table->integer('brew_method_id')->unsigned();
$table->foreign('brew_method_id')->references('id')->on('brew_methods');
});
What this does is first create a cafes_brew_methods
table which will be our join table. Next it creates an unsigned integer called cafe_id
It needs to be unsigned since it’s referencing an auto incrementing field. Next we set up the relationship for that key.
A foreign key is simply a reference to a primary key on a different table. This helps keep data integrity in tact so you don’t reference cafes or brew methods that don’t exist.
To set up the relationship, we use the schema builder documented here: Database: Migrations – Laravel – The PHP Framework For Web Artisans to set up a foreign key. Looking at this line of code:
$table->foreign('cafe_id')->references('id')->on('cafes');
We first say that the newly created cafe_id
will be our foreign key and that it will reference the id
key on the cafes
table. The schema builder makes this SQL query much easier to grasp.
For the brew methods relationship, we repeat the same type of logic except we reference the brew_methods
table.
Now we have our many-to-many table structured! We just need to instruct our models on how to use it.
Step 3: Create Brew Methods Model
In your /app/Http/Models/
directory add a file BrewMethods.php
. This will be the model we use for our Brew Methods table.
First we need to declare our model and add the table it uses:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BrewMethod extends Model
{
protected $table = 'brew_methods';
}
Now we need to declare the relationship to cafes
. To do this, we will be creating a method that contains our relationship so add:
public function cafes(){
}
to the model. Now we need to use the belongsToMany
relationship provided by Eloquent. In our cafes
method, we should add this code:
return $this->belongsToMany( 'App\Models\Cafe', 'cafes_brew_methods', 'brew_method_id', 'cafe_id' );
What this does is states that each brew method belongs to many cafe models on the cafes_brew_methods
table. The next two parameters are hidden in the docs for Eloquent on Laravel, but what they state is that we are using the brew_method_id
column for a Brew Method as the key we are mapping to the foreign cafe id which is represented by the cafe_id
table. I like to explicitly declare these two parameters in case there is a different key used for primary keys.
Our Brew Methods are now set up correctly, so next we should configure our Cafes.
Step 4: Configure Cafes to have many Brew Methods
If we open our app/Http/Models/Cafe.php
file, we need to add
the same type of relationship for cafes to brew methods as we did from brew methods to cafes. To do this, add this function to the Cafe.php
model:
public function brewMethods(){
return $this->belongsToMany( 'App\Models\BrewMethod', 'cafes_brew_methods', 'cafe_id', 'brew_method_id' );
}
This does exactly the same thing as we did with Brew Methods to cafes except from Cafes to brew methods, we are saying that each Cafe belongs to many brew methods on the cafes_brew_methods
table and that we are using the cafe_id
of the cafe table to bind thebrew_method_id
in the cafes_brew_methods
table to join the two models. The documentation states it here: Eloquent: Relationships – Laravel – The PHP Framework For Web Artisans
Now we have our many to many relationship set up and we are good to go for using it!
Step 5: Retrieving the Many To Many Data Record
When we load the cafes, we also want to load the brew methods associated with the Cafe. We want to do this by calling the relationship method on the model when we retrieve our data.
First, open the app/Http/Controllers/API/CafesController.php
file and look at the getCafes()
method. Right now we are calling:
$cafes = Cafe::all()
which gets all of the cafes. We want to call our relationship to the Brew Methods in this as well, so we need to change the code to:
$cafes = Cafe::with('brewMethods')->get();
Now each cafe returned will have the associated brew methods that the cafe provides!
We should also update our return for the individual cafe as well, so if you look at the getCafe( $id )
method, swap:
$cafe = Cafe::where('id', '=', $id)->first();
with
$cafe = Cafe::where('id', '=', $id)
->with('brewMethods')
->first();
Now we return the brew methods with each individual cafe as well!
Step 6: Other Features
So, along with adding our Brew Methods model and tables, I also added a route to retrieve the brew methods:
/*
|-------------------------------------------------------------------------------
| Get All Brew methods
|-------------------------------------------------------------------------------
| URL: /api/v1/brew-methods
| Controller: API\BrewMethodsController@getBrewMethods
| Method: GET
| Description: Gets all of the brew methods in the application
*/
Route::get('/brew-methods', 'API\BrewMethodsController@getBrewMethods');
A Brew Methods Controller located at: app/Http/Controllers/API/BrewMethodsController.php
which returns all of the brew methods. I followed the same ideas laid out in this tutorial: Add Laravel API End Points – Server Side Up.
I also added a Vuex module and an API file for Brew Methods. The API file is /resources/assets/js/api/brewMethod.js
and contains the route to load brew methods. For the tutorial on Javascript API routes check out: https://serversideup.net/build-api-requests-javascript/.
The Vuex module is located at /resources/assets/js/modules/brewMethods.js
and imported into the store at /resources/assets/js/store.js
. To build this module, I followed the same instructions as I did for Building a Vuex Module found here: Build a Vuex Module – Server Side Up.
The last thing I did is added the dispatch call to load the brew methods in the /resources/assets/js/pages/Layout.vue
file to load the brew methods on page load:
this.$store.dispatch( 'loadBrewMethods' );
The reason I load it on the initial page load is we can use the brew methods for filtering, but also for adding a new cafe. We will want to select the brew methods available at that cafe when adding the cafe.
Step 7: Saving Many To Many Data
So in the companion tutorial about Dynamic Forms with VueJS https://serversideup.net/dynamic-forms-vuejs/, I will be showing how to tie everything together in the NewCafe.vue
page. We will be adding another feature to cafes that allow for multiple locations that will drastically adjust the form we created and all of that is detailed in the Dynamic Forms with VueJS tutorial https://serversideup.net/dynamic-forms-vuejs/. For now, know we will be passing an array of brew method ids to the API at the /api/v1/cafes
POST route.
That route is handled by the app/Http/Controllers/API/CafesController.php
file and the postNewCafe()
method.
What we will do is after we save our new cafe, we will sync the brewMethods()
relationship with the brew_method
ids array.
Add the code just below the saving of the cafe. The cafe has to exist before we sync relationships:
/*
Sync the brew methods on the cafe
*/
$brewMethods = $request->get('brew_methods');
$cafe->brewMethods()->sync( $brewMethods );
This is all we need to do to save the brew methods with the cafe! If you were to edit a brew method like remove one, sync will take care of all of the logic for it!
Conclusion
In this tutorial we added a many to many relationship between cafes and brew methods. A cafe can have many brew methods and brew methods can have many cafes. In the next tutorial, we are going to take things one step farther with a Parent/Child relationship between cafes. We will use cafe locations as an example. A cafe may have multiple locations with different brew methods. For example, Valentine coffee in the Milwaukee airport does not have pour over, but in their tasting room they do.
All code is pushed here: GitHub – serversideup/roastandbrew Feel free to reach out with any questions!