A highly optimized role and permission package for Laravel 11/12 with caching, multiple guards, wildcard permissions, super admin, and Laravel Gate integration
saeedvir/laravel-permissions is a Laravel package for a highly optimized role and permission package for laravel 11/12 with caching, multiple guards, wildcard permissions, super admin, and laravel gate integration.
It currently has 10 GitHub stars and 18 downloads on Packagist (latest version v2.1.2).
Install it with composer require saeedvir/laravel-permissions.
Discover more Laravel packages by saeedvir
or browse all Laravel packages to compare alternatives.
Last updated
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.
posts.* to grant all post permissions$user->can() nativelyUser::role('admin')->get()# 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-permissions
The package will automatically register its service provider.
Publish the configuration file:
php artisan vendor:publish --tag=permissions-config
Update your .env file:
PERMISSION_DB_CONNECTION=mysql
PERMISSION_DB_NAME=laravel_permission
PERMISSION_CACHE_ENABLED=true
PERMISSION_CACHE_EXPIRATION=3600
Update config/permissions.php to set your database connection properly.
php artisan migrate
Or publish and customize migrations first:
php artisan vendor:publish --tag=permissions-migrations
php artisan migrate
Add 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>
@endhaspermission
Assign 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=true
use 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 role
How it works:
null expires_at never expire (permanent)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 expiration
Use 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'); // true
Designate 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');
//redirect to ./admin-login if Auth::check() === false
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware('check.auth:./admin-login');
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:
PERMISSION_CACHE_ENABLED=true in .envcomposer test
If you discover any security-related issues, please email [email protected] 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 [email protected]