spatie/laravel-permission
Database-backed roles and permissions for Laravel. Assign roles and permissions to users, sync them to the Gate, and check abilities with Laravel’s built-in can()/authorize features. Includes migrations, caching, teams, and flexible model setup.
Installation:
composer require spatie/laravel-permission
Run migrations:
php artisan migrate
Publish Config (Optional):
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
Configure config/permission.php (e.g., enable/disable teams, cache).
First Use Case: Assign a role to a user in a controller:
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
$role = Role::create(['name' => 'admin']);
$permission = Permission::create(['name' => 'edit articles']);
$role->givePermissionTo($permission);
$user = User::find(1);
$user->assignRole($role);
Check Permissions:
if ($user->can('edit articles')) {
// Grant access
}
$user->assignRole('admin'); // Single role
$user->assignRole(['editor', 'moderator']); // Multiple roles
if ($user->hasRole('admin')) { ... }
if ($user->hasAnyRole(['editor', 'moderator'])) { ... }
$user->givePermissionTo('edit articles');
$user->givePermissionTo(['create posts', 'delete comments']);
if ($user->can('edit articles')) { ... }
if ($user->can(['edit articles', 'publish posts'])) { ... }
$role = Role::findByName('editor');
$role->givePermissionTo('edit articles');
$user->syncRoles(['admin', 'editor']); // Replace all roles
$user->syncPermissions(['edit articles', 'publish posts']); // Replace all permissions
Route::get('/admin', function () {
// ...
})->middleware(['role:admin']);
public function handle(Request $request, Closure $next)
{
if (!$request->user()->can('manage users')) {
abort(403);
}
return $next($request);
}
config/permission.php:
'teams' => [
'enabled' => true,
],
$team = Team::find(1);
$team->assignRole('admin');
posts.*):
$user->givePermissionTo('posts.*');
$user->can('posts.create'); // Returns true
class PostPolicy {
public function update(User $user, Post $post) {
return $user->can('edit posts');
}
}
DatabaseSeeder to predefine roles/permissions:
public function run()
{
$admin = Role::create(['name' => 'admin']);
$admin->givePermissionTo(Permission::all());
$user = User::find(1);
$user->assignRole($admin);
}
Route::middleware(['auth:sanctum', 'can:manage users'])->group(function () {
// Admin-only routes
});
Blade::directive('permission', function ($expression) {
return "<?php if(auth()->user()->can({$expression})): ?>";
});
Usage:
@permission('edit articles')
<button>Edit</button>
@endpermission
public function handle(PermissionAttachedEvent $event)
{
Log::info("User {$event->user->name} gained permission {$event->permission->name}");
}
$user->getAllPermissions(); // Cached by default
Clear cache when needed:
php artisan permission:cache-reset
strtolower()) if needed.posts.*) can slow down checks if overused. Test performance in high-traffic apps.teams table, team_user pivot). Ensure teams.enabled is true in config.users table, ensure morphTo() relationships (for teams) are added after Spatie’s migrations.permission:cache-reset after bulk updates.->middleware('can:permission') instead of ->middleware('can:permission,name') (deprecated).PackageServiceProvider (no register method in AppServiceProvider).$permission = Permission::where('name', 'edit articles')->first();
dd($permission); // Check if null
$user->permissions; // Inspect loaded permissions
$role = Role::findByName('admin');
dd($role); // Check if null
model_has_roles):
SELECT * FROM model_has_roles WHERE model_type = 'App\Models\User' AND model_id = 1;
.* (e.g., posts.*). Test with:
$user->can('posts.create'); // Should return true if 'posts.*' is assigned
public function handle(Request $request, Closure $next)
{
Log::debug('User can:', [$request->user()->can('edit articles')]);
return $next($request);
}
Permission/Role models:
class CustomPermission extends Permission {
protected $table = 'custom_permissions';
}
Register in AuthServiceProvider:
$this->registerPolicies();
Permission::useModel(CustomPermission::class);
PermissionGuard for non-standard auth:
$guard = app('auth')->guard('api');
if ($guard->user()->can('access api')) { ... }
class CustomPermissionResolver implements PermissionResolver {
public function resolve($user, $permission) {
// Custom logic
return true;
}
}
Bind in AppServiceProvider:
$this->app->bind(PermissionResolver::class, CustomPermissionResolver::class);
PermissionAttachedEvent):
class CustomPermissionAttachedEvent extends PermissionAttachedEvent {
public function __construct(User $user, Permission $permission, array $extra) {
parent::__construct($user, $permission);
$this->extra = $extra;
}
}
AssignRoleCommand):
class CustomAssignRoleCommand extends AssignRole
How can I help you explore Laravel packages today?