Eloquent Has Many Through Relationship

The “has-many-through” relationship provides a shortcut for accessing distant relations via an intermediate relation.

We are going to have a country table which will need a new Country role which we’ll migration at the same time:

php artisan make:model Country -m

Since we need a new foreign key (country_id) on the users table, we’ll create a new migration for this:

php artisan make:migration add_country_id_column_to_users --table=users

Open up the migration and add the first to up function and second to down function:

$table->integer('country_id');
$table->dropColumn('country_id');

Now open your country table migration to add a name column:

$table->string('name');

Now migrate this

php artisan migrate

Create a few countries. Attach your user to one of these new countries by editing and inputting the ID.

Now we need to define this Many Through relationship in the model. Open up Country model and add:

    public function posts(){
        return $this->hasManyThrough('App\Models\Post','App\Models\User');
    }
The first argument passed to the hasManyThrough function is the name of the final model we wish to access, while the second argument is the name of the intermediate model which has the foreign ID of this model (country_id).

Now let’s see this lookup in action. Add this to routes:

Route::get('/user/country', function(){
    $country = Country::find(4);
    foreach($country->posts as $post){
        echo $post->title;
    }
});
We find by country ID which then finds the user that owns the post to get the post title.

Eloquent Querying Intermediate Table / Pivot

Add this to the User model.. the new addition is withPivot which allows us to pull data from the intermediate table. We always need to specify this.

    public function roles(){
        return $this->belongsToMany('App\Models\Role')->withPivot('created_at');
    }

Now in routes add this:

Route::get('/user/pivot', function(){
    $user = User::find(1);
    foreach($user->roles as $role){
        echo $role->pivot->created_at;
    }
});

Eloquent Many to Many Relationship

We are going to make roles for our users which will require a new model called Role with a migration

php artisan make:model Role -m

Next you need to make a table for both roles and users. In Laravel you need to use singular case and go in alphabetical order.

php artisan make:migration create_users_roles_table --create=role_user

In your roles migration add the new fields:

$table->string('name');

In your user role migration add these fields:

$table->integer('user_id');
$table->integer('role_id');

Now migrate this

php artisan migrate

Create two roles: administrator, subscriber. Create a second user if doesn’t exist. In role_user attach the user to role based on ID of each. You can do 1,1 and 2,2.

Now we need to define the relationship in the model. Open up User model and add:

    public function roles(){
        return $this->belongsToMany('App\Models\Role');
        //To customize tables name and columns follow the format below
        //(Table name, user foreign key name, role foreign key name)
        //return $this->belongsToMany('App\Models\Role', 'user_roles', 'user_id', 'role_id');
    }

Now let’s see roles in action. Add this to routes:

Route::get('/user/{id}/role', function($id){
    $user = User::find($id);
    foreach($user->roles as $role){
        echo $role->name.'<br>';
    }
});

Another way to find roles:

Route::get('/user/{id}/role', function($id){
    $user = User::find($id)->roles()->orderBy('id', 'desc')->get();
    return $user;
});

Eloquent One to Many Relationship

Update your Post model by adding this:

    public function posts(){
        return $this->hasMany('App\Models\Post');
    }

We can now display the user’s post titles in our routes:

use App\Models\User;

/*
|--------------------------------------------------------------------------
| ELOQUENT Relationships
|--------------------------------------------------------------------------
*/

Route::get('/posts', function(){
    $user = User::find(1);
    foreach($user->posts as $post){
        echo $post->title.'<br>';
    }
});

View in your browser.

Eloquent Inverse Relationship

Update your Post model by adding this:

    public function user(){
        return $this->belongsTo('App\Models\User');
    }

We can now display the post’s user in our routes:

use App\Models\User;

/*
|--------------------------------------------------------------------------
| ELOQUENT Relationships
|--------------------------------------------------------------------------
*/

Route::get('/post/{id}/user', function($id){
    return Post::find($id)->user->name;
});

View in your browser.

Eloquent One to One Relationship

Update your post migration to have a user_id column

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->integer('user_id')->unsigned();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });

Update your migrations with

php artisan migrate:refresh

Add a user and a post in your database, where you add the user ID to the new post.

Then update your User model by adding this:

    public function post(){
        return $this->hasOne('App\Model\Post');
    }
The user_id is used as the default connection to Post model. Add a second parameter to use a different id name. Add a third parameter to change the post ID.

Now that we’ve added this relationship, we can display the user’s post in our routes:

use App\Models\User;

/*
|--------------------------------------------------------------------------
| ELOQUENT Relationships
|--------------------------------------------------------------------------
*/

Route::get('/user/{id}/post', function($id){
    return User::find($id)->post->title;
});