Kritim Yantra
May 25, 2025
Building modern web applications often requires controlling user access to specific features. Whether it's an admin-only dashboard or content restricted by roles, Role-Based Access Control (RBAC) is the go-to solution. In this comprehensive guide, we’ll walk you through setting up Roles and Permissions in Laravel 12, covering:
✅ Database design
✅ Model relationships
✅ Middleware for access control
✅ Blade directives for views
✅ Seeding roles and permissions
Let's get started!
Role-Based Access Control (RBAC) is a system where you assign permissions to roles, and then roles to users. For example:
This setup ensures a clean, maintainable system for controlling access across your Laravel application.
We'll need the following tables:
users
table (default in Laravel)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->rememberToken();
$table->timestamps();
});
roles
tableSchema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
permissions
tableSchema::create('permissions', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
role_user
(many-to-many: users ↔️ roles)Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->primary(['role_id', 'user_id']);
});
permission_role
(many-to-many: roles ↔️ permissions)Schema::create('permission_role', function (Blueprint $table) {
$table->foreignId('permission_id')->constrained()->cascadeOnDelete();
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->primary(['permission_id', 'role_id']);
});
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function assignRole($role)
{
if (is_string($role)) {
$role = Role::whereName($role)->firstOrFail();
}
return $this->roles()->syncWithoutDetaching($role);
}
public function hasRole($role)
{
if (is_string($role)) {
return $this->roles->contains('name', $role);
}
return !! $role->intersect($this->roles)->count();
}
public function permissions()
{
return $this->roles->map->permissions->flatten()->pluck('name')->unique();
}
public function hasPermission($permission)
{
return $this->permissions()->contains($permission);
}
public function users()
{
return $this->belongsToMany(User::class);
}
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
public function givePermissionTo($permission)
{
if (is_string($permission)) {
$permission = Permission::whereName($permission)->firstOrFail();
}
return $this->permissions()->syncWithoutDetaching($permission);
}
public function roles()
{
return $this->belongsToMany(Role::class);
}
$user = User::find(1);
$user->assignRole('admin');
$adminRole = Role::where('name', 'admin')->first();
$adminRole->givePermissionTo('edit-users');
if ($user->hasRole('admin')) {
// Admin access granted
}
if ($user->hasPermission('edit-users')) {
// Permission granted
}
Create:
php artisan make:middleware RoleMiddleware
Implement:
public function handle(Request $request, Closure $next, $role)
{
if (!auth()->user()->hasRole($role)) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
Register in Kernel.php
:
'role' => \App\Http\Middleware\RoleMiddleware::class,
Usage:
Route::get('/admin', fn() => 'Admin Panel')->middleware(['auth', 'role:admin']);
Similar steps apply:
php artisan make:middleware PermissionMiddleware
Implement:
public function handle(Request $request, Closure $next, $permission)
{
if (!auth()->user()->hasPermission($permission)) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
Add to AppServiceProvider
:
Blade::if('role', fn($role) => auth()->check() && auth()->user()->hasRole($role));
Blade::if('permission', fn($permission) => auth()->check() && auth()->user()->hasPermission($permission));
Usage:
@role('admin')
<p>Welcome, Admin!</p>
@endrole
@permission('edit-users')
<button>Edit User</button>
@endpermission
Create a seeder:
php artisan make:seeder RolesAndPermissionsSeeder
Example:
public function run()
{
$admin = Role::create(['name' => 'admin']);
$editor = Role::create(['name' => 'editor']);
$permissions = ['manage-users', 'edit-content', 'publish-content'];
foreach ($permissions as $perm) {
Permission::create(['name' => $perm]);
}
$admin->givePermissionTo(Permission::all());
$editor->givePermissionTo(['edit-content', 'publish-content']);
}
Run:
php artisan db:seed --class=RolesAndPermissionsSeeder
✅ Use Enums for role/permission names.
✅ Cache permissions for performance.
✅ Build an admin UI for roles & permissions.
✅ Add audit logs for changes.
✅ Write tests for authorization logic.
By following this guide, you now have a powerful, flexible, and scalable Roles & Permissions system in Laravel 12! 🚀
✅ Database relationships? ✔️
✅ Model methods? ✔️
✅ Middleware & Blade directives? ✔️
✅ Seeding data? ✔️
Keep building and securing your Laravel apps with confidence! 💪
Transform from beginner to Laravel expert with our personalized Coaching Class starting June 20, 2025. Limited enrollment ensures focused attention.
1-hour personalized coaching
Build portfolio applications
Industry-standard techniques
Interview prep & job guidance
Complete your application to secure your spot
Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google