Skip to main content
Tutorial Free

Laravel Many-to-many Pivot Table: Add Extra Column with Relation

January 25, 2023
2 min read

Sometimes you need to add more fields to the many-to-many pivot table, and those fields may also have their own relationships.

Example: you have models User, Project, and Role.

Users may be assigned to different Projects, with a different Role in each project.

The pivot table structure would look like this:

project_user

  • project_id (foreign key to projects)
  • user_id (foreign key to users)
  • role_id (foreign key to roles)

If that third field is a regular field, without a relationship, you define this in the Eloquent model:

app/Models/Project.php:

class Project extends Model {
 
public function users() {
return $this->belongsToMany(User::class)->withPivot('role_id');
}
 
}

But since we have a relationship here, we can define a special Pivot model and add a relationship inside of it.

php artisan make:model ProjectUser --pivot

Then, in that Pivot model, you define the relationship to the Role.

app/Models/ProjectUser.php:

use Illuminate\Database\Eloquent\Relations\Pivot;
 
class ProjectUser extends Pivot
{
public function role()
{
return $this->belongsTo(Role::class, 'role_id');
}
}

Then, you provide that Pivot model as a part of your many-to-many relationship, with using():

app/Models/Project.php:

use App\Models\ProjectUser;
 
class Project extends Model {
 
public function users() {
return $this->belongsToMany(User::class)
->withPivot('role_id')
->using(ProjectUser::class);
}
 
}

And then, finally, you can run this query:

$projects = App\Models\Project::with('users')->get();
foreach ($projects as $project) {
echo $project->name. ': ';
foreach ($project->users as $user) {
echo $user->name . ' (' . $user->pivot->role->name . ') ';
}
}

As you can see, we can access the role of each user by calling $user->pivot->role->name.

Notice: keep in mind that this code will unfortunately produce the N+1 query problem. During my testing, I haven't found a way to eager load the users.pivot.role with this or any other syntax. If you have achieved this, please write in the comment, and I will update the article with your solution.

Enjoyed This Tutorial?

Get access to all premium tutorials, video and text courses, and exclusive Laravel resources. Join our community of 10,000+ developers.

Comments & Discussion

BH
Bilal Haidar ✓ Link copied!

Hi Povilas, I would rename the many-to-many table as engagements.

Then, I create a Model Engagement. This model should have 3 relationsships belongsToMany for the Project, User, and Role. I would specify the engagements as the Pivot table.

Then, I query the Engagement model, join on Products, Users, and Roles, and select the columns I want.

class Engagement extends Model
{
public function projects()
{
return $this->belongsToMany(Project::class, 'engagements');
}
public function users()
{
return $this->belongsToMany(User::class, 'engagements');
}
public function roles()
{
return $this->belongsToMany(Role::class, 'engagements');
}
}
 
$engagements = Engagement::join('projects','engagements.project_id','projects.id')
->join('users','engagements.user_id','users.id')
->join('roles','engagements.role_id','roles.id')
->select('engagements.id','projects.title', 'users.name', 'roles.name')
->get();

That's what i would use for 3 tables having a many-to-many relationship. I am not 100% sure that's what you are looking for, but thought of sharing it.

PK
Povilas Korop ✓ Link copied!

Wow, thanks for sharing! Not sure I would do it exactly this way, but you have a good logic behind it.

D
dascorp ✓ Link copied!

Have a look at medium article and this repo to solve N+1

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.