Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Jamessks's avatar

Role-Permission relationship problem

In a policy of a PostPolicy file for a "create" permission. I am trying to get this to work:

    public function create(User $user)
    {
		$permissions = $user->roles()->permissions()->get());
		// Look for a specific permission and if exists return true else return false.
    }

The idea is to make sure the user's role has a create-post permission so as to allow them to visit the "post/create" view and then execute the PostController "store()" function.

However all I am getting is the following Error: Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsToMany::permissions()

I was expecting to get a list of all permissions based on the role of the user but i am not for some unknown reason.

For my scenario I am using 5 tables. users, roles, permissions, role_user, permission_role

The models used User, Role, Permission

Their relations are as follows.

User Model:

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

Role Model:

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

    public function permissions()
    {
        return $this->belongsToMany(Permission::class);
    }

Permission Model:

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

So the idea is, a user may have multiple roles and roles may have different multiple permissions.

Some things that I have in mind:

Yeah sure I could just get the User's role with

$permission = $user->roles()->get()->value('id');

fetch the role_id of the user and then

Role::find($permission)->permissions()->pluck('name')

to fetch all the role's permissions etc. but that just seems a bit too much and too verbose. I can't shake the feeling that this could be done in a much simpler and elegant way.

So any sort of input on why this doesn't work?

 $user->roles()->permissions()->get()
0 likes
11 replies
kevinbui's avatar
kevinbui
Best Answer
Level 41

Nope, it is not possible working with Laravel Eloquent that way. That error is spot on.

With Dynamic Relationships and Eloquent Collections we can do this:

    public function create(User $user)
    {
		$permissions = $user
            ->roles
            ->map(fn (Role $role) => $role->permissions)
            ->collapse()
            ->unique();

        return $permissions->contains('name', 'create-post');
    }

Or we can do everything in one go, with a bit of higher order messages

    public function create(User $user)
    {
		return $user
            ->roles
            ->map
            ->permissions
            ->collapse()
            ->unique()
            ->contains('name', 'create-post');
    }

Let me know if those work.

Apparently you are using your own system of authorization. You might have heard about Bouncer and Spatie permissions. Those could make features like this more streamlined.

1 like
Jamessks's avatar

@kevinbui I see. this does seem to work. I am indeed aware of the spatie package and in a realistic scenario I would look no further but really i'm still in the learning steps of laravel so i'm just playing around for now as in not making anything real or serious. Thank you for the reply.

Ben Taylor's avatar

Why would users have multiple roles? That just seems to make things way more complicated than the they need to be. You are over engineering a pretty simple thing.

1 like
Jamessks's avatar

@Ben Taylor I mean it depends on the project complexity but I just wanted to force a many to many relationship just for practice. Thank you for your input.

Snapey's avatar

@Ben Taylor being able to assign multiple roles actually makes things much more flexible and avoids creating new roles that have mostly the same permissions as another role but with one extra permission.

Either that or you need the option to assign permissions directly to a user with a permission_user table

1 like
Ben Taylor's avatar

@Snapey I kinda see your point. You would certainly end up with fewer roles. But at the cost of unnecessary complexity I would argue. I haven't run into any limitations with a single role relationship, but I'm open to having my mind changed.

1 like
Snapey's avatar

@Ben Taylor obviously not developed for organisations. Imagine a small business with an accountant and a buyer. They have roles of the same name. Now suppose the accountant goes on holiday for two weeks, and the buyer needs to ensure some of his suppliers get paid. Do you add the required permissions to the buyer, giving other buyers the same access or do you give the head buyer temporary access to the accounting role? Of course they will still need buying permissions.

A contrived example, but quite common scenario

Similar to @jlrdw post

1 like
Ben Taylor's avatar

@Snapey Thanks for the example. That helps me see where you are coming from better. That makes a lot of sense actually. In a single role architecture, the same thing can be achieved by creating a new role with combined permissions. This is potentially annoying in that you would have more roles created. On the flip side, you may not want your buyer who is filling in for the accountant to have all the permissions of an accountant (only some of them). My way offers that flexibility. For yours to have that flexibility, you would have to create a new role too (e.g. limited accountant role). So at the end of the day, both ways achieve the same results. I just still think mine saves on complexity.

jlrdw's avatar

Mutiple roles are used at times.

Example I've used in the past.

  • Bob is an admin

  • Suzy is admin and does bookkeeping

  • Mary is a bookkeeper only

  • If Bob is logged in, Bob can only do admin stuff and all access to user stuff. But Bob cannot mess with bookkeeping.

  • If Suzy is logged in she can access admin stuff and bookkeeping and accounting stuff.

  • If Mary is logged in she cannot mess with admin stuff, but has access to bookkeeping and accounting stuff.

So I just check at method level if the logged in users role can or cannot access that method / function.

And use query scopes to let a user edit / view their own data or an admin can access all users data.

Each app will be different as to who can do what.

So in pseudocode:

public function makeInvoice()
    {
        if (a required role of bkeep is not true here) {   // bkeep = bookkeeper
            return redirect('somewhere'); // whereever you redirect to if not authorized
        }
        // Rest of method here is accomplished if 
        // the logged in user has the required role of 'bkeep'.
    }

Again just examples.

Also a Spatie example I saw:

public function update(Request $request, Post $post) {
    if ($post->author !== auth()->user()->id || auth()->user()->cannot('edit posts'))
        abort(404);// or redirect, or whatever action 
    }
    //rest of method if all okay
}

In summary RBAC is at least 3 main steps:

  • A login required
  • An authorization implementation to determine what the logged in person with role can or cannot do
  • Protection of URL and parameters, checking that the logged in users id matches the id used in a query

Your main concern is:

  • Okay a user is logged in (Authenticated)
  • But what can they do or not do
  • A method or route to a method has a lock (just example)
  • If their role is the key they can enter

So there are times when a user has multiple roles like in my example, but concern yourself about the method.

  • Does one of their roles allow entry, yes or no.

@martinbean also has an article on roles:

https://laracasts.com/discuss/channels/laravel/laravel-role-based-authentication-system?page=1&replyId=930281

Me I like protecting (authorize) at method level.

I see if a role matches a required role to "enter a method" like:

    public static function chkRole($role = null)
    {
        $userrole = Auth::user()->role;
        $checkrole = explode(',', $userrole);
        if (in_array($role, $checkrole)) {
            return true;
        }
        return false;
    }

From a helper class.

I use laravels authentication (login required), but I use my custom authorization.

I do not recommend custom to anyone unless they have a lot of experience however.

1 like
jlrdw's avatar

@Jamessks Hope you get it worked out. ACL or RBAC can be one of the trickiest things to learn and implement, but once learned like anything it gets easier.

Edit:

Don't forget to look over @martinbean role article.

1 like

Please or to participate in this conversation.