Introduction to Roles and Permissions in Laravel

Access control is one of the most essential aspects of modern web applications. Whether you are building a content management system, an e-commerce platform, a learning management system, or an enterprise application, you often need to restrict access to certain features based on user roles or permissions. Laravel’s built-in authorization features are powerful, but when you need a scalable, advanced, and highly flexible role-based permission system, the Spatie Laravel Permission package is the industry standard.

The Spatie Laravel Permission package brings an elegant and expressive way to assign roles, check permissions, restrict access, and manage authorization logic in any Laravel application. It integrates deeply with Laravel’s policies and gates, provides full database management for roles and permissions, supports caching for performance, and works seamlessly with guards and teams.

This comprehensive 3000-word guide covers everything you need to know about the Spatie Laravel Permission package, including installation, configuration, database tables, assigning roles, granting permissions, middleware usage, blade directives, artisan commands, multiple guards, teams, model scopes, caching, and best practices. If you want to build a role-based system in Laravel that is both elegant and powerful, this article will guide you through every detail.

What Is the Spatie Laravel Permission Package

The Spatie Laravel Permission package is an open-source package developed by Spatie, a company known for producing high-quality Laravel packages. It provides:

  • Role management
  • Permission management
  • Assigning roles to users
  • Giving permissions to users or roles
  • Middleware for routing protection
  • Caching for speed
  • Support for teams or multi-tenancy
  • Database-backed access control
  • Integration with guards

The package allows you to attach roles to any model (commonly the User model) and assign permissions to either roles or users directly. It is fully compatible with Laravel’s authorization features.


Why Use Spatie for Roles and Permissions

There are several reasons why this package has become the most widely used role management system in Laravel projects:

  • Simple and intuitive API
  • Easily integrates with Laravel
  • Supports unlimited roles and permissions
  • Allows permission inheritance through roles
  • Provides expressive methods like
    assignRole(), hasRole(), can(), hasPermissionTo()
  • Enables permission caching for performance
  • Supports multi-guard authentication
  • Works with teams or organizations
  • Amazing documentation and long-term maintenance

For projects requiring flexible and scalable access control, this package is the perfect solution.


Installing the Package

Installing the package is extremely simple. In your Laravel project directory, run:

composer require spatie/laravel-permission

Once installed, publish the configuration file and migrations:

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

This will publish the migration files and the configuration file located at:

config/permission.php

Next, run the migrations:

php artisan migrate

You now have the database tables required for roles and permissions.


Database Tables Created by the Package

After running migrations, several tables are created:

roles
permissions
model_has_roles
model_has_permissions
role_has_permissions

Each table serves a specific purpose:

  • roles: Stores all defined roles
  • permissions: Stores all permissions
  • model_has_roles: Links users or other models to roles
  • model_has_permissions: Links users or models to permissions
  • role_has_permissions: Connects roles to specific permissions

These tables allow complete flexibility in managing authorization.


Adding the HasRoles Trait to Your User Model

To use roles and permissions, add the HasRoles trait to your User model.

Open app/Models/User.php:

use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
use HasRoles;
}

This adds methods like assignRole(), removeRole(), hasRole(), givePermissionTo(), revokePermissionTo(), and can().


Creating Roles

You can create roles using Eloquent or Artisan commands.

Create a role using the model:

use Spatie\Permission\Models\Role;

Role::create(['name' => 'admin']);
Role::create(['name' => 'editor']);
Role::create(['name' => 'user']);

Roles represent user categories or access levels.


Creating Permissions

Just like roles, permissions can be created using the model:

use Spatie\Permission\Models\Permission;

Permission::create(['name' => 'edit articles']);
Permission::create(['name' => 'publish articles']);
Permission::create(['name' => 'delete articles']);

Permissions represent individual abilities.


Assigning Roles to Users

To give a user a role:

$user->assignRole('admin');

You can assign multiple roles:

$user->assignRole(['editor', 'user']);

Removing Roles From Users

To remove a role:

$user->removeRole('admin');

Remove all roles:

$user->syncRoles([]);

Checking User Roles

To check if a user has a specific role:

$user->hasRole('admin');

Check multiple roles:

$user->hasAnyRole(['admin', 'editor']);

Check if user has all roles:

$user->hasAllRoles(['writer', 'editor']);

Giving Permissions to Roles

You can assign permissions to a role like this:

$role = Role::findByName('admin');
$role->givePermissionTo('edit articles');

Or multiple permissions:

$role->givePermissionTo(['edit articles', 'delete articles']);

Giving Permissions Directly to Users

$user->givePermissionTo('publish articles');

Checking User Permissions

Check if a user has permission:

$user->can('edit articles');

Or:

$user->hasPermissionTo('edit articles');

Removing Permissions

Remove permission from a user:

$user->revokePermissionTo('publish articles');

Remove permissions from a role:

$role->revokePermissionTo('delete articles');

Synchronizing Roles and Permissions

Sync roles:

$user->syncRoles(['admin', 'editor']);

Sync permissions:

$role->syncPermissions(['edit articles', 'publish articles']);

Using Middleware for Route Protection

The package provides built-in middleware for restricting routes.

Protect routes using roles:

Route::group(['middleware' => ['role:admin']], function () {
// admin only routes
});

Protect using permissions:

Route::group(['middleware' => ['permission:edit articles']], function () {
// only users with permission
});

Only allow users with any of multiple roles:

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

Using Blade Directives

Blade templates can easily check roles and permissions.

Check a role:

@role('admin')
<p>You are an admin</p>
@endrole

Check permission:

@can('edit articles')
<button>Edit</button>
@endcan

Check multiple roles:

@hasanyrole('admin|editor')
<p>Admin or Editor</p>
@endhasanyrole

Check all roles:

@hasallroles('writer|editor')
All roles match
@endhasallroles

Using Artisan Commands

Spatie provides several Artisan commands.

List all roles:

php artisan permission:show

Cache permissions:

php artisan permission:cache-reset

Clear permission cache:

php artisan permission:clear

Permission Caching

The package automatically caches permissions for performance. You can reset the cache:

php artisan permission:cache-reset

Always reset the cache after adding roles or permissions in code.


Using Multiple Guards

Laravel supports multiple guards like web and api.

To create a role for a guard:

Role::create(['name' => 'admin', 'guard_name' => 'api']);

Define guard in configuration:

'guards' => ['web', 'api']

You can assign roles to users based on the guard they authenticate with.


Using Teams or Multi-Tenancy

The package supports teams or groups for multi-tenant apps.

Enable teams:

'teams' => true

In the config file.

Then roles and permissions can be team-specific.


Restricting Permissions Within Teams

You can check permissions within a team context:

$user->hasPermissionTo('edit articles', 'web', $teamId);

Model Scopes and Role Queries

You can filter models by role:

User::role('admin')->get();

Or permission:

User::permission('edit articles')->get();

These scopes are powerful for admin dashboards.


Protecting Controllers

Inside controllers, you can protect methods:

$this->middleware('permission:edit articles')->only('edit');

Or protect entire controllers:

$this->middleware(['role:admin']);

Protecting Policies

Spatie integrates well with policies.

Inside a policy method:

return $user->hasRole('admin');

Or:

return $user->can('edit articles');

Assigning Permissions Based on Events

You can automatically assign permissions after user registration.

Example:

User::created(function ($user) {
$user->assignRole('user');
});

Building a Full Role and Permission Dashboard

You can build management screens for:

  • Creating roles
  • Editing roles
  • Assigning permissions
  • Assigning roles to users
  • Managing team-specific permissions
  • Displaying permissions in admin UI

Spatie works well with Livewire, Inertia, and Blade.


Advanced Permission Logic in Controllers

Example checking multiple conditions:

if ($user->hasRole('admin') || $user->can('edit articles')) {
//
}

Checking for multiple permissions:

$user->hasAnyPermission(['publish articles', 'delete articles']);

Customizing Permission Models

You may customize the model classes:

'models' => [
'permission' => App\Models\CustomPermission::class,
]

Laravel will use your custom model instead of default.


Using Permission Groups

You can group permissions logically:

  • Article permissions
  • User management permissions
  • Product permissions
  • Order permissions

This is commonly done in admin panels.


Bulk Assigning Permissions

To give all permissions to a role:

$role->syncPermissions(Permission::all());

Preventing Unauthorized Access

Spatie provides clean and expressive error responses. In production, use handlers to customize the forbidden responses.


Handling Permission Errors

You can catch unauthorized exceptions:

try {
$user->checkPermissionTo('delete articles');
} catch (\Exception $e) {
//
}

Best Practices With Spatie Roles and Permissions

  • Do not assign too many permissions directly to users; use roles
  • Use syncRoles() and syncPermissions() for cleaner updates
  • Always reset cache after making database changes
  • Use middleware for route protection
  • Avoid duplicate role names
  • Store permissions in seeders for consistency
  • Use policies for advanced logic
  • Use teams for multi-tenant applications

Common Mistakes Developers Make

  • Forgetting to run php artisan migrate
  • Forgetting to add the HasRoles trait
  • Not resetting cache after updating permissions
  • Assigning permissions directly instead of using roles
  • Mixing guards incorrectly
  • Hard-coding authorization logic in controllers

Example: Full Setup of Role-Based Access Control

Create permissions:

Permission::create(['name' => 'edit articles']);
Permission::create(['name' => 'publish articles']);

Create roles:

Role::create(['name' => 'admin']);
Role::create(['name' => 'editor']);

Assign permissions to roles:

$admin->givePermissionTo(Permission::all());
$editor->givePermissionTo(['edit articles']);

Assign role to user:

$user->assignRole('admin');

Check permission:

$user->can('publish articles');

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *