Search code examples
laraveleloquentmany-to-many

Laravel Eloquent many-to-many relationship: Use explicit pivot table


How can I select an explicit table (with migration and model) as pivot table?

My models are

User
-id

Role
-id

user_role
-user_id
-role_id
-other_foreign_key_id
-other_attributes
-...

I know how many-to-many relations work in laravel in general. But in the documentation they only use the automatic generated joining table where I can't define an additional foreign key constraint or datatypes for additional columns.

What I want is that I have my own migration and model for the user_role table and use that as pivot but I don't know how to explicit link a defined table as pivot to a m-to-n relation.

Thanks in advance.


Solution

  • In addition to customizing the name of the joining table, you may also customize the column names of the keys on the table by passing additional arguments to the belongsToMany method. The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to, like this:

    return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id')
               ->withPivot(['other_foreign_key_id', 'other_attributes']);
    

    By default, only the model keys will be present on the pivot object. If your pivot table contains extra attributes, you must specify them when defining the relationship with withPivot() method as done above:

    You can use your pivot attributes using pivot like this:

    $user = User::find($id);
    foreach ($user->roles as $role) {
        echo $role->pivot->created_at;
        echo $role->pivot->other_attribute;
    }
    

    UPDATE - Custom pivot model in Eloquent

    You can also define your own custom pivot model like this:

    class RoleUserPivot extends Pivot {
        // let's use date mutator for a field
        protected $dates = ['completed_at'];
    }
    

    Now, in order to let Eloquent grab this pivot model, we also need to override newPivot method on both User and Role:

    // User model
    public function newPivot(Model $parent, array $attributes, $table, $exists)
    {
        if ($parent instanceof Role) {
            return new RoleUserPivot($parent, $attributes, $table, $exists);
        }
    
        return parent::newPivot($parent, $attributes, $table, $exists);
    }
    
    
    // Role model
    public function newPivot(Model $parent, array $attributes, $table, $exists)
    {
        if ($parent instanceof User) {
            return new RoleUserPivot($parent, $attributes, $table, $exists);
        }
    
        return parent::newPivot($parent, $attributes, $table, $exists);
    }
    

    See more about Custom Pivot Tables

    Hope this helps!