# Laravel Permission Package ## Introduction The Spatie Laravel Permission package is a powerful role-based access control (RBAC) system designed for Laravel applications. It provides a flexible and database-driven approach to managing user permissions and roles, allowing developers to define granular access controls throughout their applications. The package seamlessly integrates with Laravel's built-in authorization system by registering all permissions on Laravel's gate, enabling the use of standard authorization methods like `can()` and middleware protection. The package supports multiple authentication guards, team-based permissions, wildcard permissions, and provides eloquent model traits that can be applied to any user model. It includes comprehensive middleware for route protection, Blade directives for view-level authorization, and a caching system for optimal performance. The package is fully compatible with Laravel 8.0 through 12.0 and includes support for Passport client credentials for API authentication scenarios. ## Core Functionality ### Assigning Permissions to Users Direct permission assignment to user models. ```php use App\Models\User; use Spatie\Permission\Models\Permission; // Create a permission $permission = Permission::create(['name' => 'edit articles']); // Assign permission directly to user $user = User::find(1); $user->givePermissionTo('edit articles'); // Assign multiple permissions $user->givePermissionTo(['edit articles', 'delete articles', 'publish articles']); // Check if user has permission if ($user->hasPermissionTo('edit articles')) { // User can edit articles } // Check using Laravel's gate if ($user->can('edit articles')) { // User can edit articles } // Revoke permission $user->revokePermissionTo('edit articles'); // Sync permissions (removes all existing, sets only these) $user->syncPermissions(['edit articles', 'delete articles']); ``` ### Managing Roles Create and manage roles with associated permissions. ```php use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; use App\Models\User; // Create roles $writerRole = Role::create(['name' => 'writer']); $editorRole = Role::create(['name' => 'editor']); $adminRole = Role::create(['name' => 'admin']); // Create permissions $editPermission = Permission::create(['name' => 'edit articles']); $deletePermission = Permission::create(['name' => 'delete articles']); $publishPermission = Permission::create(['name' => 'publish articles']); // Give permissions to roles $writerRole->givePermissionTo('edit articles'); $editorRole->givePermissionTo(['edit articles', 'delete articles']); $adminRole->givePermissionTo(['edit articles', 'delete articles', 'publish articles']); // Assign role to user $user = User::find(1); $user->assignRole('writer'); // Assign multiple roles $user->assignRole(['writer', 'editor']); // Check if user has role if ($user->hasRole('writer')) { // User is a writer } // Check if user has any of the given roles if ($user->hasAnyRole(['writer', 'editor'])) { // User is either a writer or editor } // Check if user has all roles if ($user->hasAllRoles(['writer', 'editor'])) { // User has both roles } // Remove role $user->removeRole('writer'); // Sync roles (removes all existing, sets only these) $user->syncRoles(['editor']); // Get all user roles $roles = $user->getRoleNames(); // Returns collection of role names ``` ### Checking Permissions via Roles Verify permissions granted through role assignments. ```php use App\Models\User; use Spatie\Permission\Models\Role; // Create role with permissions $editorRole = Role::create(['name' => 'editor']); $editorRole->givePermissionTo(['edit articles', 'delete articles']); // Assign role to user $user = User::find(1); $user->assignRole('editor'); // Check permission (will return true via role) if ($user->hasPermissionTo('edit articles')) { // User can edit articles through the editor role } // Check multiple permissions if ($user->hasAllPermissions(['edit articles', 'delete articles'])) { // User has all specified permissions } if ($user->hasAnyPermission(['edit articles', 'publish articles'])) { // User has at least one of these permissions } // Get direct permissions only $directPermissions = $user->getDirectPermissions(); // Get permissions via roles $rolePermissions = $user->getPermissionsViaRoles(); // Get all permissions (direct + via roles) $allPermissions = $user->getAllPermissions(); // Check if permission is direct if ($user->hasDirectPermission('edit articles')) { // User has this permission directly assigned } ``` ### Route Protection with Middleware Protect routes using permission and role middleware. ```php use Illuminate\Support\Facades\Route; use Spatie\Permission\Middleware\PermissionMiddleware; use Spatie\Permission\Middleware\RoleMiddleware; use Spatie\Permission\Middleware\RoleOrPermissionMiddleware; // In app/Http/Kernel.php, register middleware aliases: // protected $middlewareAliases = [ // 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, // 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, // 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, // ]; // Protect route with permission Route::get('/articles/edit/{id}', function ($id) { // Only users with 'edit articles' permission can access return view('articles.edit', ['id' => $id]); })->middleware('permission:edit articles'); // Protect with multiple permissions (user needs ANY of these) Route::get('/dashboard', function () { return view('dashboard'); })->middleware('permission:view dashboard|edit dashboard'); // Protect route with role Route::get('/admin', function () { return view('admin.dashboard'); })->middleware('role:admin'); // Protect with multiple roles (user needs ANY of these) Route::post('/articles/publish', function () { // Logic here })->middleware('role:editor|admin'); // Protect with role OR permission Route::delete('/articles/{id}', function ($id) { // Logic here })->middleware('role_or_permission:admin|delete articles'); // Using specific guard Route::get('/api/admin', function () { return response()->json(['status' => 'ok']); })->middleware('permission:admin access,api'); // In controllers, use middleware in constructor class ArticleController extends Controller { public function __construct() { $this->middleware('permission:edit articles')->only(['edit', 'update']); $this->middleware('permission:delete articles')->only('destroy'); $this->middleware('role:admin')->only('publish'); } public function edit($id) { // Only accessible with 'edit articles' permission } } ``` ### Blade Directives for View Authorization Control view rendering based on permissions and roles. ```php {{-- Check if user has specific role --}} @role('writer')

You are a writer

@endrole {{-- Check if user has any of the roles --}} @hasanyrole('writer|editor') Edit Articles @endhasanyrole {{-- Check if user has all roles --}} @hasallroles('writer|editor')

You are both a writer and editor

@endhasallroles {{-- Check if user does NOT have a role --}} @unlessrole('admin')

You are not an admin

@endunlessrole {{-- Check if user has permission --}} @can('edit articles') Edit @endcan {{-- Check if user lacks permission --}} @cannot('delete articles')

You cannot delete articles

@endcannot {{-- Using specific guard --}} @role('admin', 'api')

You are an API admin

@endrole {{-- Check direct role assignment --}} @hasrole('writer') Writer badge @endhasrole {{-- Complex authorization logic --}} @can('edit articles') @role('editor') @endrole @endcan ``` ### Database Queries with Permission Scopes Query users based on roles and permissions. ```php use App\Models\User; use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; // Get all users with a specific role $editors = User::role('editor')->get(); // Get users with any of multiple roles $staff = User::role(['writer', 'editor'])->get(); // Get users WITHOUT a specific role $nonAdmins = User::withoutRole('admin')->get(); // Get users with a specific permission $canEdit = User::permission('edit articles')->get(); // Get users with any of multiple permissions $canModify = User::permission(['edit articles', 'delete articles'])->get(); // Get users WITHOUT a permission $cannotPublish = User::withoutPermission('publish articles')->get(); // Combining scopes $editorsThatCanPublish = User::role('editor') ->permission('publish articles') ->get(); // Get users with role or permission $authorized = User::role('admin') ->orWhere(function ($query) { $query->permission('manage users'); }) ->get(); // Count users with specific role $adminCount = User::role('admin')->count(); // Pagination with scopes $editors = User::role('editor')->paginate(20); ``` ### Guard-Specific Permissions Manage permissions across multiple authentication guards. ```php use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; use App\Models\User; use App\Models\Admin; // Create permissions for different guards $webEditPermission = Permission::create([ 'name' => 'edit articles', 'guard_name' => 'web' ]); $apiEditPermission = Permission::create([ 'name' => 'edit articles', 'guard_name' => 'api' ]); // Create roles for different guards $webEditor = Role::create([ 'name' => 'editor', 'guard_name' => 'web' ]); $apiEditor = Role::create([ 'name' => 'editor', 'guard_name' => 'api' ]); // Assign guard-specific permissions $webEditor->givePermissionTo($webEditPermission); $apiEditor->givePermissionTo($apiEditPermission); // Assign to users with proper guard $webUser = User::find(1); // Uses 'web' guard by default $webUser->assignRole($webEditor); $apiUser = Admin::find(1); // Uses 'api' guard $apiUser->assignRole($apiEditor); // Check permission with specific guard if ($webUser->hasPermissionTo('edit articles', 'web')) { // Web user can edit articles } // Find permission by name and guard $permission = Permission::findByName('edit articles', 'api'); // Find role by name and guard $role = Role::findByName('editor', 'web'); ``` ### Team-Based Permissions Implement multi-tenancy with team-based permission scoping. ```php use App\Models\User; use App\Models\Team; use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; // Enable teams in config/permission.php // 'teams' => true, // Set current team context $team = Team::find(1); setPermissionsTeamId($team->id); // Create team-specific role $teamEditor = Role::create([ 'name' => 'editor', 'team_id' => $team->id ]); // Create team-specific permission $teamPermission = Permission::create([ 'name' => 'edit team articles', 'team_id' => $team->id ]); // Give permission to role $teamEditor->givePermissionTo($teamPermission); // Assign role to user within team context $user = User::find(1); setPermissionsTeamId($team->id); $user->assignRole('editor'); // Check permissions within team context setPermissionsTeamId($team->id); if ($user->hasPermissionTo('edit team articles')) { // User can edit articles for this team } // Switch team context $otherTeam = Team::find(2); setPermissionsTeamId($otherTeam->id); // User's permissions are now scoped to the other team if (!$user->hasRole('editor')) { // User might not have editor role in this team } // Get current team ID $currentTeamId = getPermissionsTeamId(); // Global roles (no team restriction) $globalAdmin = Role::create([ 'name' => 'super-admin', 'team_id' => null // Global role ]); ``` ### Wildcard Permissions Use wildcard patterns for flexible permission matching. ```php // Enable in config/permission.php // 'enable_wildcard_permission' => true, use Spatie\Permission\Models\Permission; use App\Models\User; // Create wildcard permissions Permission::create(['name' => 'articles.*']); // All article permissions Permission::create(['name' => 'articles.*.own']); // Own articles only Permission::create(['name' => 'admin.*.*']); // All admin permissions // Assign wildcard permission $user = User::find(1); $user->givePermissionTo('articles.*'); // Check specific permissions (matched by wildcard) if ($user->hasPermissionTo('articles.edit')) { // Returns true - matches 'articles.*' } if ($user->hasPermissionTo('articles.delete')) { // Returns true - matches 'articles.*' } if ($user->hasPermissionTo('articles.publish')) { // Returns true - matches 'articles.*' } // More specific wildcard $moderator = User::find(2); $moderator->givePermissionTo('articles.edit.*'); if ($moderator->hasPermissionTo('articles.edit.published')) { // Returns true - matches 'articles.edit.*' } if ($moderator->hasPermissionTo('articles.delete.published')) { // Returns false - doesn't match 'articles.edit.*' } // Multi-level wildcards $admin = User::find(3); $admin->givePermissionTo('*'); if ($admin->hasPermissionTo('anything.at.all.levels')) { // Returns true - matches '*' } // Combined specific and wildcard $user->givePermissionTo(['articles.edit', 'comments.*', 'users.view']); ``` ### Checking Permissions in Policies Integrate with Laravel's policy authorization. ```php // app/Policies/ArticlePolicy.php namespace App\Policies; use App\Models\User; use App\Models\Article; class ArticlePolicy { public function update(User $user, Article $article) { // Check if user has permission if ($user->hasPermissionTo('edit articles')) { return true; } // Check if user is the author return $user->id === $article->user_id; } public function delete(User $user, Article $article) { // Admin can delete any article if ($user->hasRole('admin')) { return true; } // Others can only delete their own if they have permission return $user->hasPermissionTo('delete articles') && $user->id === $article->user_id; } public function publish(User $user, Article $article) { // Only editors and admins can publish return $user->hasAnyRole(['editor', 'admin']); } public function viewAny(User $user) { // Check if user has any viewing permissions return $user->hasAnyPermission([ 'view articles', 'edit articles', 'delete articles' ]); } } // Using in controllers class ArticleController extends Controller { public function update(Request $request, Article $article) { $this->authorize('update', $article); // Update logic here } public function destroy(Article $article) { $this->authorize('delete', $article); $article->delete(); return redirect()->route('articles.index'); } } ``` ### Caching and Performance Optimization Manage permission cache for optimal performance. ```php use Spatie\Permission\PermissionRegistrar; use Spatie\Permission\Models\Role; use Illuminate\Support\Facades\Artisan; // Clear the permission cache manually app(PermissionRegistrar::class)->forgetCachedPermissions(); // Or use artisan command Artisan::call('permission:cache-reset'); // Configure cache in config/permission.php // 'cache' => [ // 'expiration_time' => \DateInterval::createFromDateString('24 hours'), // 'key' => 'spatie.permission.cache', // 'store' => 'default', // Use specific cache driver // ], // Cache is automatically cleared when: // - Roles are created, updated, or deleted // - Permissions are created, updated, or deleted // - Permissions are assigned to or removed from roles // - Permissions are assigned to or removed from users // - Roles are assigned to or removed from users // Example: Bulk operations with cache management $role = Role::findByName('editor'); // The cache will be cleared after these operations $role->givePermissionTo(['edit articles', 'delete articles', 'publish articles']); // Force cache reset after custom database operations \DB::table('role_has_permissions')->insert([ 'role_id' => $role->id, 'permission_id' => 123, ]); app(PermissionRegistrar::class)->forgetCachedPermissions(); // Use custom cache store // In config/permission.php: // 'cache' => [ // 'store' => 'redis', // Use Redis for permission cache // ], ``` ### Using Enums for Permissions and Roles Define permissions and roles using PHP enums. ```php // app/Enums/PermissionsEnum.php namespace App\Enums; enum PermissionsEnum: string { case EDIT_ARTICLES = 'edit articles'; case DELETE_ARTICLES = 'delete articles'; case PUBLISH_ARTICLES = 'publish articles'; case MANAGE_USERS = 'manage users'; } // app/Enums/RolesEnum.php namespace App\Enums; enum RolesEnum: string { case ADMIN = 'admin'; case EDITOR = 'editor'; case WRITER = 'writer'; case SUBSCRIBER = 'subscriber'; } // Using enums with the package use App\Enums\PermissionsEnum; use App\Enums\RolesEnum; use App\Models\User; use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; // Create permissions from enum foreach (PermissionsEnum::cases() as $permission) { Permission::create(['name' => $permission->value]); } // Create roles from enum foreach (RolesEnum::cases() as $role) { Role::create(['name' => $role->value]); } // Assign using enums $user = User::find(1); $user->givePermissionTo(PermissionsEnum::EDIT_ARTICLES); $user->assignRole(RolesEnum::WRITER); // Check using enums if ($user->hasPermissionTo(PermissionsEnum::EDIT_ARTICLES)) { // User can edit articles } if ($user->hasRole(RolesEnum::WRITER)) { // User is a writer } // Give permission to role using enum $editorRole = Role::findByName(RolesEnum::EDITOR->value); $editorRole->givePermissionTo([ PermissionsEnum::EDIT_ARTICLES, PermissionsEnum::DELETE_ARTICLES, PermissionsEnum::PUBLISH_ARTICLES, ]); // Middleware with enums Route::get('/articles/edit', function () { return view('articles.edit'); })->middleware('permission:' . PermissionsEnum::EDIT_ARTICLES->value); // In policies public function update(User $user, Article $article) { return $user->hasPermissionTo(PermissionsEnum::EDIT_ARTICLES); } ``` ### Artisan Commands Manage permissions and roles via command line. ```bash # Create a new permission php artisan permission:create-permission "edit articles" php artisan permission:create-permission "delete articles" --guard=api # Create a new role php artisan permission:create-role editor php artisan permission:create-role "api-admin" --guard=api # Show all permissions and roles php artisan permission:show # Output example: # +------------+---------------+ # | Guard | Permissions | # +------------+---------------+ # | web | edit articles | # | | delete articles| # +------------+---------------+ # # +------------+---------------+ # | Guard | Roles | # +------------+---------------+ # | web | editor | # | | writer | # +------------+---------------+ # Reset permission cache php artisan permission:cache-reset # Upgrade database schema for teams feature php artisan permission:upgrade-teams ``` ### Configuration and Installation Setup and configure the package. ```php // Install via Composer // composer require spatie/laravel-permission // Publish configuration // php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" // Run migrations // php artisan migrate // config/permission.php configuration return [ 'models' => [ 'permission' => \Spatie\Permission\Models\Permission::class, 'role' => \Spatie\Permission\Models\Role::class, ], 'table_names' => [ 'roles' => 'roles', 'permissions' => 'permissions', 'model_has_permissions' => 'model_has_permissions', 'model_has_roles' => 'model_has_roles', 'role_has_permissions' => 'role_has_permissions', ], 'column_names' => [ 'role_pivot_key' => null, // default 'role_id' 'permission_pivot_key' => null, // default 'permission_id' 'model_morph_key' => 'model_id', 'team_foreign_key' => 'team_id', ], 'register_permission_check_method' => true, 'teams' => false, // Enable team-based permissions 'enable_wildcard_permission' => false, 'display_permission_in_exception' => false, 'display_role_in_exception' => false, 'events_enabled' => false, // Enable permission/role events 'cache' => [ 'expiration_time' => \DateInterval::createFromDateString('24 hours'), 'key' => 'spatie.permission.cache', 'store' => 'default', ], ]; // Add trait to User model use Spatie\Permission\Traits\HasRoles; class User extends Authenticatable { use HasRoles; // Model code... } // Register middleware in app/Http/Kernel.php protected $middlewareAliases = [ 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, ]; ``` ## Use Cases and Integration The Laravel Permission package is designed for applications requiring sophisticated access control systems. Common use cases include multi-user content management systems where different user types (administrators, editors, writers, subscribers) need varying levels of access to content creation, editing, publishing, and deletion capabilities. E-commerce platforms benefit from role-based access for managing inventory, processing orders, handling customer support, and accessing financial reports. SaaS applications with team-based access control can leverage the teams feature to provide complete tenant isolation where each organization manages its own set of users, roles, and permissions without interference from other tenants. The package integrates seamlessly with Laravel's existing authorization infrastructure. All defined permissions are automatically registered with Laravel's Gate, allowing developers to use familiar authorization methods like `$user->can()`, `@can` Blade directives, and `$this->authorize()` in controllers. The package works alongside Laravel policies, enabling developers to combine role-based permissions with resource-specific authorization logic. Middleware integration provides route-level protection, while Blade directives offer fine-grained view-level access control. The caching system ensures that permission checks remain performant even in applications with complex permission hierarchies, and the event system allows applications to react to permission changes for audit logging or real-time notifications.