A highly optimized role and permission package for Laravel 11/12 with advanced features including multiple guards, wildcard permissions, super admin, expirable permissions, expirable roles, and Laravel Gate integration.
- âś… Role-based Access Control (RBAC)
- âś… Permission Management
- âś… Direct User Permissions
- âś… Polymorphic Relationships - Works with any model
- 🚀 Multiple Guards Support - Separate permissions for web, api, admin
- 🎯 Wildcard Permissions - Use
posts.*to grant all post permissions - đź‘‘ Super Admin Role - Automatically has ALL permissions
- ⏰ Expirable Permissions - Set expiration dates on permissions
- ⏰ Expirable Roles - Set expiration dates on roles (NEW in v2.1.0)
- đź”— Laravel Gate Integration - Use
$user->can()natively - 📊 Query Scopes -
User::role('admin')->get() - đź”’ Database Transactions - Atomic permission changes
- ⚡ Advanced Caching with Redis tags support
- đź’ľ Memory Optimized with eager loading
- đźš„ Database Optimized with composite indexes
- 📦 Multiple Database Support
- 🛡️ Middleware Protection for routes
- 🎨 Blade Directives for templates
- 📝 Comprehensive Documentation
- âś… Laravel 11/12 Compatible
- PHP 8.2 or higher
- Laravel 11.x or 12.x
- Composer
# Install package
composer require saeedvir/laravel-permissions
# Publish config
php artisan vendor:publish --tag=permissions-config
# Run migrations
php artisan migrate
# Add trait to User model and start using!Install the package via Composer:
composer require saeedvir/laravel-permissionsThe package will automatically register its service provider.
Publish the configuration file:
php artisan vendor:publish --tag=permissions-configUpdate your .env file:
PERMISSION_DB_CONNECTION=mysql
PERMISSION_DB_NAME=laravel_permission
PERMISSION_CACHE_ENABLED=true
PERMISSION_CACHE_EXPIRATION=3600Update config/permissions.php to set your database connection properly.
php artisan migrateOr publish and customize migrations first:
php artisan vendor:publish --tag=permissions-migrations
php artisan migrateAdd the HasRolesAndPermissions trait to your User model:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Saeedvir\LaravelPermissions\Traits\HasRolesAndPermissions;
class User extends Authenticatable
{
use HasRolesAndPermissions;
// ... rest of your User model
}use Saeedvir\LaravelPermissions\Models\Role;
use Saeedvir\LaravelPermissions\Models\Permission;
// Create roles
$admin = Role::create([
'name' => 'Administrator',
'slug' => 'admin',
'description' => 'Administrator role with full access'
]);
$editor = Role::create([
'name' => 'Editor',
'slug' => 'editor',
'description' => 'Editor role'
]);
// Create permissions
$createPost = Permission::create([
'name' => 'Create Post',
'slug' => 'create-post',
'description' => 'Can create posts'
]);
$editPost = Permission::create([
'name' => 'Edit Post',
'slug' => 'edit-post',
'description' => 'Can edit posts'
]);
$deletePost = Permission::create([
'name' => 'Delete Post',
'slug' => 'delete-post',
'description' => 'Can delete posts'
]);// Give permissions to role
$admin->givePermissionTo('create-post', 'edit-post', 'delete-post');
$editor->givePermissionTo('create-post', 'edit-post');
// Or using Permission models
$admin->givePermissionTo($createPost, $editPost, $deletePost);
// Revoke permission
$editor->revokePermissionTo('edit-post');
// Sync permissions (removes old, adds new)
$editor->syncPermissions(['create-post']);$user = User::find(1);
// Assign role
$user->assignRole('admin');
// Assign multiple roles
$user->assignRole('admin', 'editor');
// Or using Role models
$user->assignRole($admin, $editor);
// Assign role with expiration (NEW in v2.1.0)
$user->assignRoleUntil('premium', now()->addMonth());
$user->assignRoleUntil('trial-user', now()->addDays(7));
// Remove role
$user->removeRole('editor');
// Sync roles (removes old, adds new)
$user->syncRoles(['admin']);$user = User::find(1);
// Give direct permission to user
$user->givePermissionTo('create-post');
// Give multiple permissions
$user->givePermissionTo('create-post', 'edit-post');
// Give permission with expiration
$user->givePermissionToUntil('create-post', now()->addWeek());
// Revoke permission
$user->revokePermissionTo('edit-post');
// Sync permissions
$user->syncPermissions(['create-post']);$user = User::find(1);
// Check if user has role (automatically filters expired roles)
if ($user->hasRole('admin')) {
// User is admin
}
// Check multiple roles (any)
if ($user->hasAnyRole(['admin', 'editor'])) {
// User has at least one of these roles
}
// Check multiple roles (all)
if ($user->hasAllRoles(['admin', 'editor'])) {
// User has all these roles
}
// Check permission (includes permissions from active roles, filters expired)
if ($user->hasPermission('create-post')) {
// User can create posts
}
// Check multiple permissions (any)
if ($user->hasAnyPermission(['create-post', 'edit-post'])) {
// User has at least one of these permissions
}
// Check multiple permissions (all)
if ($user->hasAllPermissions(['create-post', 'edit-post'])) {
// User has all these permissions
}
// Get all user permissions (direct + from active roles)
$permissions = $user->getAllPermissions();@role('admin')
<p>You are an administrator!</p>
@endrole
@hasrole('admin')
<p>You are an administrator!</p>
@endhasrole
@permission('create-post')
<a href="/posts/create">Create Post</a>
@endpermission
@haspermission('create-post')
<a href="/posts/create">Create Post</a>
@endhaspermissionAssign roles with expiration dates for temporary access:
In config/permissions.php or .env:
'expirable_roles' => [
'enabled' => env('PERMISSION_EXPIRABLE_ROLES_ENABLED', false),
],Or in .env:
PERMISSION_EXPIRABLE_ROLES_ENABLED=trueuse Carbon\Carbon;
// Assign temporary role
$user->assignRoleUntil('premium', now()->addMonth());
$user->assignRoleUntil('trial-user', now()->addDays(7));
$user->assignRoleUntil('seasonal-mod', Carbon::parse('2025-12-31'));
// Using role ID or model
$user->assignRoleUntil(1, now()->addWeeks(2));
$role = Role::where('slug', 'editor')->first();
$user->assignRoleUntil($role, now()->addMonths(6));
// All role checks automatically filter expired roles
$user->hasRole('premium'); // Returns false after expiration
$user->hasPermission('premium-feature'); // Also checks role expiration
// Query scopes also respect expiration
User::role('premium')->get(); // Only users with active premium roleHow it works:
- Expired roles are automatically filtered from all role checks
- Permissions from expired roles are not granted
- Roles with
nullexpires_at never expire (permanent) - Works seamlessly with caching system
Similar to expirable roles, you can set expiration on direct permissions:
'expirable_permissions' => [
'enabled' => env('PERMISSION_EXPIRABLE_ENABLED', false),
],// Give temporary permission
$user->givePermissionToUntil('create-post', now()->addWeek());
$user->givePermissionToUntil('beta-feature', now()->addDays(30));
// Permission automatically expires
$user->hasPermission('create-post'); // Returns false after expirationUse wildcards for flexible permission matching:
'wildcard_permissions' => [
'enabled' => env('PERMISSION_WILDCARD_ENABLED', false),
],// Grant wildcard permission
$role->givePermissionTo('posts.*');
// Matches all post permissions
$user->hasPermission('posts.create'); // true
$user->hasPermission('posts.edit'); // true
$user->hasPermission('posts.delete'); // trueDesignate a role that automatically has all permissions:
'super_admin' => [
'enabled' => env('PERMISSION_SUPER_ADMIN_ENABLED', false),
'role_slug' => env('PERMISSION_SUPER_ADMIN_SLUG', 'super-admin'),
],// Assign super admin role
$user->assignRole('super-admin');
// User now has ALL permissions
$user->hasPermission('any-permission'); // Always true
// Check if user is super admin
if ($user->isSuperAdmin()) {
// User has unlimited access
}Powerful query scopes for filtering users:
// Get users with specific role
User::role('admin')->get();
User::role(['admin', 'editor'])->get();
// Get users with specific permission
User::permission('create-post')->get();
User::permission(['create-post', 'edit-post'])->get();
// Get users without role
User::withoutRole('banned')->get();
// Get users without permission
User::withoutPermission('delete-post')->get();
// Combine scopes
User::role('editor')
->permission('create-post')
->where('status', 'active')
->get();The package provides three middlewares:
Checks if user is authenticated:
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware('check.auth');Checks if user has specific role(s):
// Single role
Route::get('/admin', function () {
return view('admin.dashboard');
})->middleware('role:admin');
// Multiple roles (user needs at least one)
Route::get('/admin', function () {
return view('admin.dashboard');
})->middleware('role:admin|super-admin');
// In route groups
Route::middleware(['role:admin'])->group(function () {
Route::get('/users', [UserController::class, 'index']);
Route::get('/settings', [SettingController::class, 'index']);
});Checks if user has specific permission(s):
// Single permission
Route::post('/posts', [PostController::class, 'store'])
->middleware('permission:create-post');
// Multiple permissions (user needs at least one)
Route::put('/posts/{post}', [PostController::class, 'update'])
->middleware('permission:edit-post|edit-own-post');
// In route groups
Route::middleware(['permission:manage-posts'])->group(function () {
Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);
});Route::middleware(['check.auth', 'role:admin', 'permission:delete-post'])
->delete('/posts/{post}', [PostController::class, 'destroy']);Control caching behavior in config/permissions.php:
'cache' => [
'enabled' => env('PERMISSION_CACHE_ENABLED', true),
'expiration_time' => env('PERMISSION_CACHE_EXPIRATION', 3600), // in seconds
'key_prefix' => 'saeedvir_permissions',
'store' => env('PERMISSION_CACHE_STORE', 'default'),
],Configure unauthorized/unauthenticated responses:
'middleware' => [
'unauthorized_response' => [
'type' => 'json', // 'json', 'redirect', 'abort'
'redirect_to' => '/unauthorized',
'abort_code' => 403,
'json_message' => 'Unauthorized access.',
],
'unauthenticated_response' => [
'type' => 'redirect', // 'json', 'redirect', 'abort'
'redirect_to' => '/login',
'abort_code' => 401,
'json_message' => 'Unauthenticated.',
],
],'performance' => [
'eager_loading' => true, // Enable eager loading for relationships
'chunk_size' => 1000, // Chunk size for batch operations
],use Saeedvir\LaravelPermissions\Services\PermissionCache;
$cache = app(PermissionCache::class);
// Clear specific user cache
$cache->clearUserCache($userId);
// Clear specific role cache
$cache->clearRoleCache($roleId);
// Flush all permission caches
$cache->flush();The package automatically clears relevant caches when:
- Roles are assigned/removed from users
- Permissions are assigned/removed from users or roles
- Roles or permissions are deleted or updated
- roles - Stores role definitions
- permissions - Stores permission definitions
- role_has_permissions - Pivot table for role-permission relationships
- model_has_roles - Polymorphic pivot table for user-role relationships
- model_has_permissions - Polymorphic pivot table for user-permission relationships
- Enable Caching: Set
PERMISSION_CACHE_ENABLED=truein.env - Use Eager Loading: The package uses eager loading when checking permissions from roles
- Database Indexing: All tables have proper indexes for fast queries
- Use Slugs: Always use slugs (strings) instead of IDs for better cache utilization
- Chunk Large Operations: Use the configured chunk size for batch operations
composer testIf you discover any security-related issues, please email saeed.es91@gmail.com instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.
For support, please open an issue on GitHub or contact saeed.es91@gmail.com