Kritim Yantra
Dec 20, 2025
“Okay… users can log in, but now what?”
“How do I stop normal users from accessing admin features?”
“Why does everyone online explain this differently?”
Trust me—you’re not alone.
Most beginners hit this wall right after authentication. Login and registration feel great… until you realize real apps need rules.
In this Laravel 2026 tutorial, we’ll walk through roles and permissions like a human would explain it to a friend—step by step, no magic, no confusion.
By the end, you’ll know:
Think of your app like an office building :
Roles = Job titles
(Admin, Editor, User, Manager)
Permissions = Keys
(Create post, Edit post, Delete user, View reports)
A role is just a collection of permissions.
👉 Instead of saying “John can edit posts, delete posts, approve posts…”
You say: “John is an Editor.”
Much cleaner. Much safer.
| Concept | Meaning | Example |
|---|---|---|
| Role | Group of abilities | Admin, User, Editor |
| Permission | Single ability | create-post, delete-user |
| User | Has role(s) | John = Admin |
| System | Checks permission | Can John delete users? |
Pro Tip
Roles answer “who you are”
Permissions answer “what you can do”
This is where beginners get overwhelmed, so let’s simplify.
1️⃣ Hardcoded role checks (simple, not scalable)
2️⃣ Role column in users table
3️⃣ Custom roles & permissions tables
4️⃣ Spatie Laravel Permission package (industry standard)
We’ll cover all four, then I’ll tell you which one to use and why.
Example:
@if(auth()->user()->is_admin)
// show admin button
@endif
Or:
if(auth()->user()->role === 'admin') {
// allow
}
Warning ️
This approach works for tiny apps only. Once you have more than 2 roles, it becomes technical debt.
php artisan make:migration add_role_to_users_table
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('user');
});
Now your users table has:
id | name | email | role
if(auth()->user()->role === 'admin') {
// admin access
}
php artisan make:middleware IsAdmin
public function handle($request, Closure $next)
{
if (auth()->user()->role !== 'admin') {
abort(403);
}
return $next($request);
}
This is where things start feeling professional.
Tables:
usersrolespermissionsrole_user (pivot)permission_role (pivot)roles
id | name
permissions
id | name
role_user
user_id | role_id
permission_role
permission_id | role_id
// User.php
public function roles() {
return $this->belongsToMany(Role::class);
}
// Role.php
public function permissions() {
return $this->belongsToMany(Permission::class);
}
auth()->user()
->roles
->pluck('permissions')
->flatten()
->contains('name', 'edit-post');
This works—but:
Pro Tip
If you’re learning deeply, this is great.
If you’re building a real app? There’s a better way.
This is the industry standard.
Used in:
Clean API
Battle-tested
Supports:
composer require spatie/laravel-permission
Publish config & migrations:
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
This creates:
rolespermissionsmodel_has_rolesmodel_has_permissionsrole_has_permissionsIn User.php:
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
}
That’s it. No magic—just traits.
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
Permission::create(['name' => 'create-post']);
Permission::create(['name' => 'edit-post']);
Permission::create(['name' => 'delete-post']);
$admin = Role::create(['name' => 'admin']);
$editor = Role::create(['name' => 'editor']);
$admin->givePermissionTo(Permission::all());
$editor->givePermissionTo(['create-post', 'edit-post']);
$user->assignRole('admin');
Or multiple roles:
$user->assignRole(['admin', 'editor']);
if(auth()->user()->can('delete-post')) {
// allowed
}
Blade:
@can('edit-post')
<button>Edit</button>
@endcan
Middleware:
Route::get('/admin', function () {
//
})->middleware('permission:access-admin');
Or role-based:
->middleware('role:admin')
This is why Spatie wins.
Readable. Maintainable. Scalable.
Roles are just containers.
Permissions control real power.
Use:
@can('delete-user')
Not:
@if(auth()->user()->role === 'admin')
Faster app, fewer DB queries.
Good:
edit-post
delete-user
view-dashboard
Bad:
p1
canEditSomething
Never create them manually in production.
| Project Type | Recommendation |
|---|---|
| Learning basics | Role column |
| Small app | Role column + middleware |
| Medium app | Spatie Permission |
| Large / SaaS | Spatie Permission (100%) |
If you remember one thing from this blog:
👉 Use Spatie for real projects.
Yes! Especially with Spatie. One user can be both editor and manager.
Use both—but check permissions, not roles.
Yes. It’s widely used, actively maintained, and production-ready.
I’ve built Laravel apps where roles were an afterthought—and fixing them later was painful.
If you’re starting a project today:
What confuses you the most about Laravel roles and permissions—database design, middleware, or deciding which approach to use? Share your struggle, and I’ll help you untangle it.
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google
Kritim Yantra
Kritim Yantra