Search code examples
phplaraveleloquentmany-to-manylaravel-8

Laravel Eloquent many-to-many not saving key


I'm attempting to build a simple many-to-many relationship in Laravel 8 but I'm running into an odd problem. I'm building the fairly standard User/Roles relationship but with one difference: my primary key on those two tables is a UUID rather than an integer.

There aren't any errors but when I attach a role to a user with $user->roles()->attach($userRole); the data saved in the role_user linking table is missing the user_id, the role_id is inserted correctly. I originally had a problem where the role_id wasn't saving either but I worked out that was down to specifying protected $keyType = 'string'; on the models.

What I can't work out is if this is being caused by me using UUID's or I've done something else fundamentally wrong.

User Model

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protected $primaryKey = 'id';
    protected $keyType = 'string';

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];
    
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    protected static function boot()
    {
        parent::boot();
        self::creating(function ($model) {
            $model->id = (string)Str::uuid();
        });
    }

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

RoleUser Model

class RoleUser extends Pivot
{
    use HasFactory;

    protected $primaryKey = 'id';
    protected $keyType = 'string';

    protected static function boot()
    {
        parent::boot();
        self::creating(function ($model) {
            $model->id = (string)Str::uuid();
        });
    }
}

What I end up with, in the DB is the following.

db results

User / Role assignment code

    $adminRole = Role::where('name', 'admin')->first();
    $userRole = Role::where('name', 'user')->first();

    $admin = User::create(['name' => 'Admin User', 'email' => '[email protected]', 'password' => Hash::make('adminpass')]);
    $admin->save();
    $user = User::create(['name' => 'User User', 'email' => '[email protected]', 'password' => Hash::make('userpass')]);
    $user->save();

    $admin->roles()->attach($adminRole);
    $user->roles()->attach($userRole);
    $user->save();
    $admin->save();

I'm really lost here, possibly because I'm new to Laravel.


Solution

  • So if anybody else runs into the above issue the solution I found was to:

    When building the table in the migration make sure to set the uuid as primary! $table->uuid('id')->primary();

    And the models should all be setup as follows:

    protected $primaryKey = 'id';
    protected $keyType = 'string';
    public $incrementing = false;
    

    That above combination seems to have resolved my issue. It seemed to be building the User model but not assigning it's UUID (despite it being saved in the database). If I were to simply reload the model using $user = User::where('email', '=', '[email protected]'); it would then work perfectly fine as the model picked up the ID.

    Hopefully this helps somebody else out in the future!