Laravel Many To Many Get element that I dont have a relationship with

In laravel Many to Many relationship like the one in the Laravel Documentation example.
https://laravel.com/docs/9.x/eloquent-relationships#many-to-many

users
    id - integer
    name - string

roles
    id - integer
    name - string

role_user
    user_id - integer
    role_id - integer

In the Users model I have

public function roles()
{
    return $this->belongsToMany(Role::class);
}

In the Roles model I have

public function users()
{
    return $this->belongsToMany(User::class);
}

How can I get all Role that I don’t have a relationship to.

I tried thing like

$this->availableRoles = Role::doesntHave('users')->get();

But this give me all the Role that no user at all have

Any hint on this.

>Solution :

If you already have a User instance (I assume that is what $this is?), then you can simply filter the relationship:

$this->availableRoles = Role::whereDoesntHave('users', function ($subQuery) {
  return $subQuery->where('users.id', $this->id);
})->get();

This should return all Role elements, unless that Role has an association with the User ($this, $user, auth()->user(), etc.; adjust as required).

An alternative approach, filter out Role based on existing Roles:

$this->availableRoles = Role::whereNotIn('id', $this->roles->pluck('id'))->get();

This approach get’s the roles.id for the User instance.

You can take a look at query efficiency and use the one that works best, but either approach should work fine.

Note: If $this is not a User instance, adjust:

$user = User::find($userId); // or `auth()->user()`, etc.

$availableRoles = Role::whereDoesntHave('users', function ($subQuery) use ($user) {
  return $subQuery->where('users.id', $user->id);
})->get();

// OR

$availableRoles = Role::whereNotIn('id', $user->roles->pluck('id'))->get();

Leave a Reply