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

Ligonsker's avatar

Can't use Spatie Laravel-Permission, is my implementation OK for my basic needs?

I can't use Spatie's package because of business requirements

but what I need to do is to protect routes in web.php against certain roles.

Currently the design seem to be wrong:

There is only 1 table called permission_groups, which is simply a relationship between a user_id and a permission_name:

user_id | permission_name

Then the following code is used to create the role middleware:

<?php
namespace App\Http\Middleware;
use Closure;
class EnsureUserHasRole
{
public function handle($request, Closure $next, $role)
   {
if (! $request->user()->hasRole($role)) {
// Redirect...
       }
return $next($request);
   }
}

( Source: https://laravel.com/docs/9.x/middleware#middleware-parameters )

So with the current design, what's happening is that the name of the permission turns to also be the name of the role (So every user that is assigned this permission also has this "role")

I was thinking to add another table - roles table, and rename the above table to permissions. Then, I will make a relationship between the user_id and the role_name instead of the permission. And in the permissions table I will change the relationship to be between the permission_name and role:

//table 1:
permission_id | permission_name
//table 2:
role | permission_id
//table3:
user_id | role

Then in the web.php I will protect routes like so:

Route::middleware(['role:managers,users'])->group(function () {
});

Route::middleware(['role:admin'])->group(function () {
});

Would that be better than the current design? Or any other suggestions?

Thanks

0 likes
10 replies
jlrdw's avatar

I normally use authorization to control who can see and do things, but groups also work. Spatie uses laravel's authorization anyway, so just study how it's done there and implement yourself.

1 like
Ligonsker's avatar

@jlrdw yes! You read my mind, I decided to actually completely ditch the above idea and move to authorization with gates

Ligonsker's avatar

@jlrdw Yes, you mean this? https://laracasts.com/series/laravel-8-from-scratch/episodes/69

Because it uses Gates, but very basic, not with sub routes and many roles and permissions. Also the usage of can is restricted there to only the role(admin), and not the actual abilities of can

So it still doesn't answer what is better in case of more complex needs where there are many roles and permissions, and sub-routes. So still not sure if I should use the can: or role: or something else

Snapey's avatar

code to permissions not roles.

roles are only for granting permissions, you should use the permission against individual services.

1 like
Ligonsker's avatar

@Snapey You are correct, I will then make a new post because I also want to change it to authorization with gates and not per role

Ligonsker's avatar

@Snapey Ok but wait now that I think about it, why to permissions? What if I want roles and each role gathers number of permissions? then it should still be roles no? Then each role is just a bunch of permissions, but permissions themselves are just values connected to specific role.

So I can do both: roles and permissions, So sometimes I want to allow a user to see a resource according to his role, but sometimes I want to be more specific and allow some special user to see this resource based on single permission given to him only (so I can do both)

jlrdw's avatar

@Ligonsker make it simple, for example:

  • 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 another 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

Each application will require unique tweaks in RBAC, no two apps are exactly the same.

But just examples.

Please or to participate in this conversation.