Lightweight, cache-friendly Role & Permission management for Laravel. Minimal surface, fast lookups, predictable caching.
spatie/laravel-permission is full-featured, but sometimes you just want a tiny, pragmatic layer: assign roles, attach permissions (direct or via roles), cache results, move on. This package focuses on:
- Simplicity (one user model, no guards complexity by default)
- Tiny codebase (easy to audit & test)
- Cache effectiveness (invalidates only what changed)
- Straightforward API naming
- Simple
Role&PermissionEloquent models - Automatic slug creation via
Str::slug()usage paths - Direct user permissions + role-based permissions merged
- Cache layer (per user & per role) with manual flush command
- Minimal middleware (
role:andperm:) for routes - Framework friendly: no macro overrides, no policies required
composer require your-vendor/light-roles-laravelFor local development before publishing to Packagist you can use a path repository in your main app's composer.json.
php artisan vendor:publish --provider="LightRoles\LightRolesServiceProvider" --tag=light-roles-config
php artisan vendor:publish --provider="LightRoles\LightRolesServiceProvider" --tag=light-roles-migrations| Table | Purpose |
|---|---|
| roles | Role definitions (id, name, slug) |
| permissions | Permission definitions (id, name, slug) |
| role_permission | Pivot: role to permission |
| user_role | Pivot: user to role |
| user_permission | Pivot: direct user permissions (optional usage) |
Add the trait to your User model:
use LightRoles\Traits\HasLightRoles;
class User extends Authenticatable {
use HasLightRoles;
}Assign roles & permissions:
use App\Models\User;
$user = User::find(1);
$user->assignRole('admin');
$user->givePermission('edit-post');
if ($user->hasRole('admin')) {
// ...
}
if ($user->canPermission('edit-post')) {
// ...
}Give a permission to a role:
use LightRoles\Models\Role;
$role = Role::create(['name' => 'Editor', 'slug' => 'editor']);
$role->givePermission('publish-post');Register middleware aliases in app/Http/Kernel.php:
protected $routeMiddleware = [
// ...
'role' => \LightRoles\Http\Middleware\EnsureRole::class,
'perm' => \LightRoles\Http\Middleware\EnsurePermission::class,
];Use in routes:
Route::get('/admin', fn () => 'ok')->middleware('role:admin');
Route::post('/post', fn () => 'ok')->middleware('perm:publish-post');- Per-user caches: roles, direct permissions, merged permissions.
- Per-role cache: permissions.
- On any assign/revoke, only relevant keys are flushed.
- Configurable store / ttl / prefix.
return [
'user_model' => App\Models\User::class,
'cache_store' => env('LIGHT_ROLES_CACHE_STORE', null), // null => default cache
'cache_ttl' => 3600, // seconds
'cache_prefix' => 'lr:',
'tables' => [
'roles' => 'roles',
'permissions' => 'permissions',
'role_permission' => 'role_permission',
'user_role' => 'user_role',
'user_permission' => 'user_permission',
],
];php artisan light-roles:cache:clear --user=123Without --user it warns (global flush intentionally not implemented to avoid store scans).
$user->assignRole('admin');
$this->assertTrue($user->hasRole('admin'));- Policy helper /
can()bridge - Domain events (RoleAssigned, PermissionRevoked, etc.)
- Optional multi-guard support flag
- Benchmark script & size metrics
See NEXT_STEPS.md for extended ideas.
- Purposefully avoids polymorphic or guard abstraction until explicitly needed.
- Keeps pivot tables narrow (no timestamps) for faster writes.
- Cache keys are deterministic:
lr:roles:{id},lr:direct_perms:{id},lr:perms:{id},lr:role_perms:{roleId}.
PRs welcome. Please open an issue first for substantial changes. Consider adding tests for any new behavior.
MIT