Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here
Premium Members Only
Join to unlock this tutorial and all of our courses.
Tutorial Premium Tutorial

Laravel Roles and Permissions: Middleware, Gates or Policies?

March 14, 2023
10 min read

When creating an application, you will need some restrictions for your users. Laravel offers a variety of ways how to implement this. In this tutorial, I will show you four examples:

  • Simple Middleware
  • Restriction with Gates
  • From Gates to Policies
  • Roles in DB with Model

There are also well-known packages like spatie/laravel-permission, but for the purpose of this article, I deliberately want to show what Laravel offers in its core, without external packages.


Scenario Setup

In this example, we will work with Users and Tasks and allow different users to access different pages related to the tasks.

Here's our setup:

Migrations

Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('is_admin')->default(0);
$table->rememberToken();
$table->timestamps();
});
 
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('name');
$table->date('due_date');
$table->timestamps();
});

Now, let's define the relationships:

app/Models/User.php

use App\Models\Task;
 
// ...
 
public function tasks()
{
return $this->hasMany(Task::class);
}

app/Models/Task.php

use App\Models\User;
 
// ...
 
public function user()
{
return $this->belongsTo(User::class);
}

Example 1. Middleware: Different Pages by User Role

In this example, we'll separate our routes/models and controllers by user role. It means that we will have two pages - one for the simple user and one for the admin, and we will restrict it with Middleware.

So, we generate this Middleware class:

php artisan make:middleware IsAdmin

app/Http/Middleware/IsAdmin.php

public function handle(Request $request, Closure $next): Response
{
if (!auth()->check() || !auth()->user()->is_admin) {
abort(403);
}
return $next($request);
}

As you can see, we just have the field users.is_admin in the DB and filter by that.

Next, we need to...

Premium Members Only

This advanced tutorial is available exclusively to Laravel Daily Premium members.

Premium membership includes:

Access to all premium tutorials
Video and Text Courses
Private Discord Channel

Comments & Discussion

M
Moazam ✓ Link copied!

Would you consider using roles table or a field role enum('admin','user','manager') in your app for roles if it's just roles?

PK
Povilas Korop ✓ Link copied!

Personally, I have enum fields in the DB cause they are hard to change in the future, I prefer separate DB tables whenever possible. But it's my personal preference.

JH
Jeroen Herritsma ✓ Link copied!

What if you have to check the database through the policy and you have like 20 buttons with the same gate around it?

I've been using a session with the permissions in there on login so i can just check the session instead of queries. But maybe there is a better way im not aware of?

PK
Povilas Korop ✓ Link copied!

After you define Gates/Policies, they then are loaded from the memory not from the database every time. Of course, it depends on the situation, so without looking at how you're actually checking those 20 buttons, I can't really comment more.

��
Ігор Штогрин ✓ Link copied!

Nice guide!

SF
Steve Fontaine ✓ Link copied!

Hello, first thanks for this tutorial.

I came up with a situation where I am not sure how to use the policies. I have a route to test email notification, but I want to open it only to super_admin (I am using filament shield which user Spatie permission). I have created an EmailPolicy with the method test() in it and the appropriate return. My question is how do I set up the policy registration inside the Service Provider as it is not linked to any model I have in my domain (there is no Model Email) ? Should I just use a get definition ? How would you handle it ? Thanks a lot.

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.