The `spatie/laravel-permission` package is a robust and highly popular library for Laravel that provides a database-driven way to manage user permissions and roles. It allows you to define roles (e.g., 'admin', 'editor', 'viewer') and permissions (e.g., 'create posts', 'edit users', 'delete comments') and then assign these roles and permissions to your application's users.
Key features and concepts:
1. Roles and Permissions: You can create any number of roles and permissions. A role is essentially a collection of permissions, making it easier to manage sets of abilities. Users can be assigned multiple roles, and roles can have multiple permissions.
2. Direct Permissions: Besides assigning permissions through roles, you can also assign specific permissions directly to a user. This provides granular control when a user's abilities don't fit a predefined role.
3. Database Driven: All roles and permissions are stored in your database, making them dynamic and manageable through your application's interface (if you choose to build one).
4. Integration with Laravel's Auth: It seamlessly integrates with Laravel's existing authentication system, extending your `User` model with traits that provide methods for checking roles and permissions.
5. Easy Checking: It provides intuitive methods to check if a user 'has a role', 'has a permission', 'can do something', or 'is an admin'. These checks can be performed in controllers, views (using Blade directives), and even middleware.
6. Guard Compatibility: Supports multiple guards, allowing you to manage permissions for different authentication contexts (e.g., 'web' for users, 'api' for tokens, 'admin' for a separate admin panel).
7. Blade Directives: Offers convenient Blade directives like `@role`, `@hasrole`, `@can`, and `@hasanyrole` for conditionally displaying content in your views based on user permissions or roles.
8. Artisan Commands: Includes Artisan commands to easily create roles and permissions from the command line.
How it works (Simplified):
* You install the package and run migrations, which create `roles` and `permissions` tables, along with pivot tables to link them to users.
* You add a trait (`HasRoles`) to your `User` model.
* You use methods like `$user->assignRole('admin')` or `$user->givePermissionTo('edit posts')` to grant abilities.
* You check abilities using methods like `$user->hasRole('admin')`, `$user->can('edit posts')`, or in Blade: `@can('edit posts')`.
`spatie/laravel-permission` greatly simplifies the implementation of authorization logic in Laravel applications, making it a go-to solution for developers needing robust permission management.
Example Code
```php
<?php
// --- 1. Installation (in your project directory) ---
// composer require spatie/laravel-permission
// --- 2. Publish Migrations and Config (after installation) ---
// php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="permission-migrations"
// php artisan migrate
// php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="permission-config"
// --- 3. Add the HasRoles Trait to your User Model (app/Models/User.php) ---
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles; // <-- Add this line
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles; // <-- Add HasRoles here
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
}
// --- 4. Example Usage: Creating Roles & Permissions and Assigning to Users (e.g., in a Seeder or Controller) ---
// php artisan make:seeder RolesAndPermissionsSeeder
// database/seeders/RolesAndPermissionsSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class RolesAndPermissionsSeeder extends Seeder
{
public function run()
{
// Reset cached roles and permissions
app()["cache"]->forget("spatie.permission.cache");
// Create Permissions
Permission::create(['name' => 'create posts']);
Permission::create(['name' => 'edit posts']);
Permission::create(['name' => 'delete posts']);
Permission::create(['name' => 'publish posts']);
Permission::create(['name' => 'manage users']);
// Create Roles and assign existing permissions
$adminRole = Role::create(['name' => 'admin']);
$adminRole->givePermissionTo(Permission::all());
$editorRole = Role::create(['name' => 'editor']);
$editorRole->givePermissionTo(['create posts', 'edit posts', 'publish posts']);
$viewerRole = Role::create(['name' => 'viewer']);
$viewerRole->givePermissionTo(['view posts']); // Assuming 'view posts' exists or is handled by default
// Create some users and assign roles/permissions
$user1 = User::factory()->create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'password' => bcrypt('password')
]);
$user1->assignRole('admin');
$user2 = User::factory()->create([
'name' => 'Editor User',
'email' => 'editor@example.com',
'password' => bcrypt('password')
]);
$user2->assignRole('editor');
// Give editor an additional direct permission not part of the role
$user2->givePermissionTo('delete posts');
$user3 = User::factory()->create([
'name' => 'Viewer User',
'email' => 'viewer@example.com',
'password' => bcrypt('password')
]);
$user3->assignRole('viewer');
$user4 = User::factory()->create([
'name' => 'Guest User',
'email' => 'guest@example.com',
'password' => bcrypt('password')
]);
// Assign direct permission without a role
$user4->givePermissionTo('create posts');
}
}
// To run the seeder: php artisan db:seed --class=RolesAndPermissionsSeeder
// --- 5. Checking Roles and Permissions ---
// In a Controller (e.g., app/Http/Controllers/PostController.php)
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class PostController extends Controller
{
public function __construct()
{
// Middleware example: Only users with 'edit posts' permission can access edit methods
// $this->middleware('permission:edit posts', ['only' => ['edit', 'update']]);
// Middleware example: Only users with 'admin' role can access certain methods
// $this->middleware('role:admin', ['only' => ['destroy']]);
}
public function index()
{
// No specific permission required to view all posts
return view('posts.index');
}
public function create()
{
// Check if the authenticated user has 'create posts' permission
if (auth()->user()->can('create posts')) {
return view('posts.create');
}
abort(403, 'You do not have permission to create posts.');
}
public function store(Request $request)
{
// Policy or direct check
$this->authorize('create posts'); // Throws 403 automatically if not allowed
// ... logic to store post ...
return redirect()->route('posts.index')->with('success', 'Post created successfully!');
}
public function edit($id)
{
// Check if user has 'edit posts' permission or 'admin' role
if (auth()->user()->hasPermissionTo('edit posts') || auth()->user()->hasRole('admin')) {
// ... retrieve post and show edit form ...
return view('posts.edit', ['post' => $post]);
}
abort(403, 'You do not have permission to edit this post.');
}
public function destroy($id)
{
// Only an admin can delete posts (example logic)
if (auth()->user()->hasRole('admin')) {
// ... logic to delete post ...
return redirect()->route('posts.index')->with('success', 'Post deleted successfully!');
}
abort(403, 'Only administrators can delete posts.');
}
public function showUserPermissions(User $user)
{
echo "User: ".$user->name."<br>";
echo "Roles: ".implode(', ', $user->getRoleNames()->toArray())."<br>";
echo "Permissions (direct): ".implode(', ', $user->getDirectPermissions()->pluck('name')->toArray())."<br>";
echo "Permissions (via roles): ".implode(', ', $user->getPermissionsViaRoles()->pluck('name')->toArray())."<br>";
echo "All Permissions: ".implode(', ', $user->getAllPermissions()->pluck('name')->toArray())."<br>";
die;
}
}
// In a Blade View (e.g., resources/views/posts/index.blade.php)
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Posts') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
@can('create posts') {{-- Using @can directive --}}
<a href="{{ route('posts.create') }}" class="btn btn-primary mb-3">Create New Post</a>
@endcan
<h3>All Posts</h3>
<ul>
<li>Post 1
@can('edit posts')
<a href="#" class="btn btn-sm btn-info">Edit</a>
@endcan
@role('admin') {{-- Using @role directive --}}
<a href="#" class="btn btn-sm btn-danger">Delete</a>
@endrole
</li>
<li>Post 2
@can('edit posts')
<a href="#" class="btn btn-sm btn-info">Edit</a>
@endcan
@hasrole('admin') {{-- Using @hasrole directive (alias for @role) --}}
<a href="#" class="btn btn-sm btn-danger">Delete</a>
@endhasrole
</li>
</ul>
@hasanyrole(['admin', 'editor']) {{-- Using @hasanyrole for multiple roles --}}
<p>You have administrative or editorial privileges.</p>
@else
<p>You only have viewer privileges or less.</p>
@endhasanyrole
@unlessrole('viewer') {{-- Using @unlessrole --}}
<p>You are not just a viewer.</p>
@endunlessrole
@if(auth()->user()->hasRole('admin')) {{-- Using direct method call --}}
<p>Hello, Admin!</p>
@endif
</div>
</div>
</div>
</div>
</x-app-layout>
// In web.php (for routes with middleware)
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth'])->group(function () {
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
Route::get('/posts/{id}/edit', [PostController::class, 'edit'])->name('posts.edit');
Route::delete('/posts/{id}', [PostController::class, 'destroy'])->name('posts.destroy');
// Using route middleware (can also be done in controller constructor)
Route::get('/admin/dashboard', function () {
return 'Admin Dashboard';
})->middleware('role:admin');
Route::get('/editor/panel', function () {
return 'Editor Panel';
})->middleware('permission:publish posts');
Route::get('/user/{user}/permissions', [PostController::class, 'showUserPermissions']);
});
```








spatie/laravel-permission