Managing Roles and Permissions in Laravel

Move aside Spatie, I’ve come to make something faster!

Italo Baeza Cabrera
3 min readJul 18, 2023

I honestly like Spatie’s library for managing roles and permissions in Laravel, but one thing I can’t fathom to understand is why everything is done through the database.

Most of the time, you declare roles and permissions in your application. By having roles and permissions set in stone, not only you evade the “database tax” of querying the initial data, but also make it immutable and easy to modify later.

That’s why I took the liberty of creating a new Composer package called Laragear Permissions.

Permissions are cool, but Roles are better

The way Laragear Permissions works is very simple: you state your roles in your application, each one with a group of permissions.

use Laragear\Permissions\Facades\Role;

Role::name('salesman')->can([
'see own orders',
'create order',
'change order items'
]);

Role::name('cashier')->basedOn('salesman')->can([
'see all orders',
'cancel order',
'complete order',
'change order items',
'change order payment',
]);

As you can see, the permissions can be just simple sentences. We can also use the basedOn() method to create a new role using the permissions of another role, saving us some keystrokes.

Once done, these are loaded into the application when it boots. From there, by adding a simple trait to the User class, your users can be granted or denied a given role.

use App\Models\User;

$user = User::where('name', 'Melissa')->first();

$user->role('cashier')->grant();

It also supports granting (or denying) explicit permissions. This is usually not recommended because Roles gives you better control, but sometimes you will need finer adjustments to some users.

use App\Models\User;

$user = User::where('name', 'Melissa')->first();

$user->permission('see inventory')->grant();

After you have set your users with their roles and permissions, checking for these is easy as pie with isGranted() and isDenied().

use App\Models\User;

$user = User::where('name', 'Melissa')->first();

if ($user->role('manager')->isGranted()) {
return 'You are the manager!';
}

if ($user->permission('see inventory')->isDenied()) {
return 'You cannot see the inventory of this store!';
}

These checks doesn’t meddle the default Laravel Authorization layer. In other words, you can easily integrate these checks into Laravel Gates and Policies as you see fit.

use App\Models\User;
use App\Models\Order;

public function show(User $user, Order $order)
{
// Check if the user is the author of the order, or can see all.
return $order->author()->is($user)
|| $user->permission('see all orders')->isGranted();
}

So later, you can use the @can and @cannot directives in your Blade views, or integrate them into Form Requests.

Believe or not, all of these permission checks never hit the database. After the first retrieval, the whole authorization data for the user sleeps in the application cache, and it’s only refreshed when the data changes. Also, these operations are atomic, meaning, there are no data-race problems when trying to change the roles or permissions of a given user at the same time.

All of this makes managing roles and permissions blazing fast, convenient, and consistent. This approach frees the database for other queries like your articles, products, and whatever, and makes them immutable by default.

There are a lot of goodies to manage permissions, and more incoming as more sponsors subscribe. I hope this package is useful for those wanting a simple but fast alternative for roles and permissions in Laravel.

--

--

Italo Baeza Cabrera

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor. https://italobc.com