hosseinhezami/laravel-permission-manager
Installation: Run the one-liner to install, migrate, and configure:
php artisan permission-manager:install --migrate
This auto-publishes config, migrations, and adds the PermissionTrait to your User model.
First Permission: Create a wildcard permission for all admin routes:
php artisan permission:create "admin.*"
First Role: Create an admin role and assign the permission:
php artisan role:create admin "Administrator" "Full system access"
php artisan role:assign-permission admin "admin.*"
Assign to User:
Attach the role to a user (replace 1 with a user ID):
php artisan user:assign-role 1 admin
Protect a Route:
Add middleware to a route in routes/web.php:
Route::get('/admin/dashboard', [AdminController::class, 'dashboard'])
->middleware('pm:role:admin');
Check in Blade: Use directives in your views:
@hasRole('admin')
<a href="/admin/dashboard">Admin Dashboard</a>
@endhasRole
php artisan to see all available commands.app/Http/Kernel.php for the pm middleware group.PermissionTrait is added to your User model.config/permission-manager.php for cache duration and wildcard settings.php artisan role:create for initial setup.php artisan role:assign-permission admin "users.*,posts.*"
$user->assignRole(['admin', 'editor']);
pm:role: or pm:permission: for route-level checks:
Route::group(['middleware' => ['auth', 'pm:role:admin|manager']], function () {
// Admin-only routes
});
admin.*) and assign them to roles to avoid per-route middleware clutter.@hasPermission('posts.create')
<button class="btn btn-primary">Create Post</button>
@endhasPermission
@foreach(PermissionManager::permissions()->list() as $permission)
@hasPermission($permission->route)
<li><a href="{{ route($permission->route) }}">{{ $permission->route }}</a></li>
@endhasPermission
@endforeach
php artisan permission:sync-routes
app/Console/Kernel.php:
protected function commands()
{
$this->call('permission:sync-routes');
}
PermissionTrait for user-specific checks:
if ($user->hasPermission('users.edit')) {
// Grant edit access
}
PermissionManager for programmatic control:
$permissions = PermissionManager::user($userId)->permissions();
api guard). Specify the guard in middleware:
Route::middleware(['auth:api', 'pm:permission:api.orders.*'])->group(...);
config/permission-manager.php (cache_duration) for production. Clear cache after permission changes:
php artisan cache:clear
PermissionManager facade in tests:
public function test_admin_can_access_dashboard()
{
$user = User::factory()->create();
$user->assignRole('admin');
$this->actingAs($user)
->get('/admin/dashboard')
->assertOk();
}
php artisan role:export roles_backup.json
php artisan permission:export permissions_backup.json
Wildcard Overuse:
*.* can lead to unintended permission overlaps.admin.* instead of *.*) and test thoroughly.Permission Sync Conflicts:
permission:sync-routes may overwrite existing permissions.--dry-run (if available) to preview changes.Cache Invalidation:
PermissionManager::clearCache().Middleware Misconfiguration:
pm:role instead of pm:role:admin).->middleware('pm:role:admin|editor')
Trait Conflicts:
PermissionTrait to a model that already extends another trait with conflicting methods.roles()) and resolve via trait composition or aliasing.Permission Checks:
if (PermissionManager::user($userId)->hasPermission('users.edit')) {
logger()->debug('User has permission: users.edit');
}
log_denials in config to log failed checks.Artisan Errors:
--verbose for detailed command output:
php artisan role:create admin --verbose
PermissionDoesNotExist or RoleDoesNotExist exceptions when assigning permissions/roles.Route Sync Issues:
php artisan route:list
permission:sync-routes is run after adding new routes.Custom Models:
Role, Permission) by publishing and modifying the migrations:
php artisan vendor:publish --provider="HosseinHezami\PermissionManager\PermissionManagerServiceProvider" --tag="migrations"
Additional Fields:
roles or permissions table (e.g., is_active):
Schema::table('roles', function (Blueprint $table) {
$table->boolean('is_active')->default(true);
});
PermissionManager facade to support the new field:
PermissionManager::roles()->where('is_active', true)->list();
Custom Guards:
PermissionGuard class to support custom logic (e.g., tenant-aware permissions):
use HosseinHezami\PermissionManager\Contracts\Guard;
class TenantGuard implements Guard
{
public function checkPermission($user, $permission)
{
// Add tenant logic here
return $user->tenant->hasPermission($permission);
}
}
Event Listeners:
PermissionManager::roles()->created(function ($role) {
event(new RoleCreated($role));
});
Blade Extensions:
Blade::directive('hasAnyPermission', function ($permissions) {
return "<?php if (auth()->check() && auth()->user()->hasAnyPermission({$permissions})): ?>";
});
Usage:
@hasAnyPermission(['users.edit', 'posts.delete'])
<!-- Content -->
@endhasAnyPermission
user_roles and `roleHow can I help you explore Laravel packages today?