https://github.com/amdad121/guard-laravel
A powerful, flexible, and developer-friendly role and permission management system for Laravel applications.
https://github.com/amdad121/guard-laravel
acl authorization laravel package permission permissions role role-based-access-control roles
Last synced: 2 months ago
JSON representation
A powerful, flexible, and developer-friendly role and permission management system for Laravel applications.
- Host: GitHub
- URL: https://github.com/amdad121/guard-laravel
- Owner: amdad121
- License: mit
- Created: 2024-01-09T20:57:49.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2026-03-20T05:23:49.000Z (3 months ago)
- Last Synced: 2026-03-20T08:42:43.103Z (3 months ago)
- Topics: acl, authorization, laravel, package, permission, permissions, role, role-based-access-control, roles
- Language: PHP
- Homepage:
- Size: 110 KB
- Stars: 13
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
# ๐ก๏ธ Guard - Modern Role & Permission Management for Laravel
[](https://packagist.org/packages/amdadulhaq/guard-laravel)
[](https://github.com/amdad121/guard-laravel/actions?query=workflow%3Arun-tests+branch%3Amain)
[](https://github.com/amdad121/guard-laravel/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
[](https://packagist.org/packages/amdadulhaq/guard-laravel)
[](https://php.net)
[](https://laravel.com)
> A powerful, flexible, and developer-friendly role and permission management system for Laravel applications.
## ๐ Quick Start
Get up and running in 5 minutes:
> **Upgrading from an older version?** Check the [Upgrade Guide](UPGRADE.md) for detailed migration instructions.
### 1. Install via Composer
```bash
composer require amdadulhaq/guard-laravel
```
### 2. Publish and run migrations
```bash
php artisan vendor:publish --tag="guard-migrations"
php artisan migrate
```
### 3. Setup your User model
Choose one setup:
**Roles only**
```php
get('/admin', [AdminController::class, 'index']);
```
## โจ Features
- ๐ฏ **Modern PHP & Laravel** - Built for PHP 8.2+ and Laravel 10/11/12/13
- ๐ **Flexible Permission System** - Users can have permissions via roles
- ๐ญ **Wildcard Permissions** - Use `posts.*` to match all post-related permissions
- โก **Smart Caching** - Automatic cache invalidation for optimal performance
- ๐ **Laravel Gate Integration** - Native `@can`, `@canany`, `@cannot` support
- ๐ก๏ธ **Middleware Protection** - `role`, `permission`, and `role_or_permission` middleware
- ๐จ **Blade Directives** - `@role`, `@hasrole`, `@hasanyrole`, `@hasallroles`
- ๐ฆ **Type-Safe Enums** - IDE-friendly `PermissionType` and `CacheKey` enums
- ๐ฐ **Guarded Roles** - Protect critical roles from accidental deletion
- ๐ **Permission Groups** - Organize permissions by resource
- ๐จ **Interactive Commands** - Laravel Prompts for creating roles/permissions
- ๐งน **Clean Architecture** - Separated concerns with traits and contracts
- ๐งช **Developer Tools** - Pint, Pest, Rector, and Larastan included
## ๐ Table of Contents
- [Installation](#installation)
- [Upgrade Guide](UPGRADE.md)
- [Configuration](#configuration)
- [Usage](#usage)
- [User Setup](#user-setup)
- [Creating Roles](#creating-roles)
- [Creating Permissions](#creating-permissions)
- [Wildcard Permissions](#wildcard-permissions)
- [Role Management](#role-management)
- [Permission Management](#permission-management)
- [Checking Access](#checking-access)
- [Middleware](#middleware)
- [Gate Integration](#gate-integration)
- [Blade Directives](#blade-directives)
- [Artisan Commands](#artisan-commands)
- [Query Scopes](#query-scopes)
- [Models Reference](#models-reference)
- [Exceptions](#exceptions)
- [Caching](#caching)
- [Database Structure](#database-structure)
- [Enums](#enums)
- [Development](#development)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
## ๐ฆ Installation
### Requirements
- **PHP**: 8.2, 8.3, 8.4, or 8.5
- **Laravel**: 10.x, 11.x, 12.x, or 13.x
- **Database**: MySQL 5.7+, PostgreSQL 9.6+, SQLite 3.8+, or SQL Server 2017+
### Step 1: Install via Composer
```bash
composer require amdadulhaq/guard-laravel
```
### Step 2: Publish Migrations
```bash
php artisan vendor:publish --tag="guard-migrations"
php artisan migrate
```
This creates 4 tables:
- `roles` - Role definitions
- `permissions` - Permission definitions
- `permission_role` - Role-permission relationships
- `role_user` - User-role relationships
Pivot table names are derived from model table names; the defaults shown above are used unless you customize model tables.
### Step 3: Configure User Model
You can use the package in two ways.
**Roles only**
```php
[
'user' => \App\Models\User::class,
'role' => \AmdadulHaq\Guard\Models\Role::class,
'permission' => \AmdadulHaq\Guard\Models\Permission::class,
],
'tables' => [
'roles' => 'roles',
'permissions' => 'permissions',
],
'cache' => [
'enabled' => env('GUARD_CACHE_ENABLED', true),
'roles_duration' => (int) env('GUARD_ROLES_CACHE_DURATION', 3600),
'permissions_duration' => (int) env('GUARD_PERMISSIONS_CACHE_DURATION', 3600),
],
'middleware' => [
'role' => 'role',
'permission' => 'permission',
'role_or_permission' => 'role_or_permission',
],
'wildcard' => [
'enabled' => env('GUARD_WILDCARD_ENABLED', true),
],
];
```
## ๐ฏ Usage
### User Setup
Use one of these setups depending on what your app needs.
**Roles only**
```php
use AmdadulHaq\Guard\Contracts\Roles as RolesContract;
use AmdadulHaq\Guard\Concerns\HasRoles;
class User extends Authenticatable implements RolesContract
{
use HasRoles;
}
```
**Roles + Permissions**
```php
use AmdadulHaq\Guard\Contracts\User as UserContract;
use AmdadulHaq\Guard\Concerns\HasRoles;
use AmdadulHaq\Guard\Concerns\HasPermissions;
class User extends Authenticatable implements UserContract
{
use HasRoles;
use HasPermissions;
}
```
Notes:
- `HasPermissions` on the user model is for permission checks.
- Users do not receive permissions directly.
- Assign permissions to roles, then users inherit them from those roles.
### Creating Roles
```php
use AmdadulHaq\Guard\Models\Role;
// Create a role
$adminRole = Role::create([
'name' => 'administrator',
'label' => 'Administrator',
'description' => 'Full system access',
'is_guarded' => true, // Protected from deletion
]);
// Create via command
// php artisan guard:create-role moderator "Moderator"
// php artisan guard:create-role moderator "Moderator" 1
// php artisan guard:create-role moderator "Moderator" user@example.com
// php artisan guard:create-role moderator "Moderator" "Jane Doe"
```
**Role Model Methods:**
```php
$role->getName(); // Get role name
$role->isProtectedRole(); // Check if guarded
$role->getPermissionNames(); // Get all permission names
$role->users; // Get users with this role
// Query scopes
Role::guarded()->get(); // Only guarded roles
Role::unguarded()->get(); // Only unguarded roles
```
### Creating Permissions
```php
use AmdadulHaq\Guard\Models\Permission;
// Simple permission
Permission::create([
'name' => 'users.create',
'label' => 'Create Users',
'description' => 'Can create new users',
'group' => 'users', // For organization
]);
// Wildcard permission (auto-sets is_wildcard = true)
Permission::create([
'name' => 'posts.*',
'label' => 'Manage All Posts',
'group' => 'posts',
]);
// Create via command
// php artisan guard:create-permission users.delete "Delete Users"
// php artisan guard:create-permission users.delete "Delete Users" 1
// php artisan guard:create-permission users.delete "Delete Users" admin
```
**Permission Model Methods:**
```php
$permission->getName(); // Get permission name
$permission->getLabel(); // Get human-readable label
$permission->getDescription(); // Get description
$permission->isWildcard(); // Check if wildcard (e.g., posts.*)
$permission->getGroup(); // Get group (e.g., 'users' from 'users.create')
$permission->getType(); // Get PermissionType enum (e.g., PermissionType::CREATE)
$permission->roles; // Get roles with this permission
// Query scopes
Permission::wildcard()->get(); // Only wildcard permissions
Permission::byGroup('users')->get(); // Permissions in users group
```
### Wildcard Permissions
Wildcard permissions automatically match all sub-permissions:
```php
// Create wildcard permission
Permission::create(['name' => 'posts.*']);
// Assign to role
$role->givePermissionTo('posts.*');
// Now user can do all of these:
$user->hasPermission('posts.create'); // true
$user->hasPermission('posts.update'); // true
$user->hasPermission('posts.delete'); // true
$user->hasPermission('posts.publish'); // true
```
The `is_wildcard` boolean is automatically set when the name ends with `*`.
### Role Management
**Assigning Roles:**
```php
// Single role
$user->assignRole('administrator'); // by role name
$user->assignRole($roleModel); // by role model
// Multiple roles in one call
$user->assignRole('administrator', 'editor');
$user->assignRole([$roleModel, $roleId, 'moderator']);
// Sync (replaces all)
$user->syncRoles(['administrator', 'editor']);
$user->syncRoles([$role1->id, $role2->id]);
// Sync without detaching existing
$user->syncRolesWithoutDetaching(['moderator']);
// Revoke
$user->revokeRole('editor');
$user->revokeRole($roleModel);
$user->revokeRoles(); // Revoke all
```
**Checking Roles:**
```php
// Single role
$user->hasRole('administrator'); // true/false
// Multiple roles
$user->hasAllRoles(['admin', 'editor']); // Must have ALL
$user->hasAnyRole(['admin', 'moderator']); // Must have ANY
// Get role names
$user->getRoleNames(); // ['administrator', 'editor']
```
### Permission Management
**Assigning to Roles:**
```php
// Single permission
$role->givePermissionTo('users.create'); // by permission name
$role->givePermissionTo($permissionModel); // by permission model
// Multiple permissions in one call
$role->givePermissionTo('users.create', 'users.edit');
$role->givePermissionTo([$permissionModel, $permissionId, 'users.delete']);
// Sync (replaces all)
$role->syncPermissions(['users.create', 'users.edit']);
$role->syncPermissions([$perm1->id, $perm2->id]);
// Revoke
$role->revokePermissionTo('users.delete');
$role->revokePermissionTo($permissionModel);
$role->revokeAllPermissions();
```
**Checking Role Permissions:**
```php
$role->hasPermissionTo('users.edit'); // Check if role has permission
$role->getPermissionNames(); // Get all permission names
```
**Checking User Permissions:**
```php
// Check by name
$user->hasPermission('users.create');
// Check by model
$user->hasPermission($permissionModel);
// Wildcard matching
$user->hasPermission('posts.*');
// Get all permissions inherited from roles
$user->getPermissions();
// Get permission names array
$user->getPermissionNames(); // ['users.create', 'users.edit']
```
### Checking Access
**Role Checking:**
```php
if ($user->hasRole('administrator')) {
// User has administrator role
}
if ($user->hasAllRoles(['admin', 'editor'])) {
// User has both roles
}
if ($user->hasAnyRole(['admin', 'moderator'])) {
// User has at least one role
}
// Get all role names
$user->getRoleNames(); // ['administrator', 'editor']
```
**Permission Checking:**
```php
if ($user->hasPermission('users.create')) {
// User can create users
}
if ($user->hasPermission('posts.*')) {
// User has wildcard permission for posts
}
```
### Middleware
All middleware supports multiple values (requires ANY):
```php
// Role middleware
Route::middleware('role:administrator')->get('/admin', [AdminController::class, 'index']);
// Multiple roles (requires ANY)
Route::middleware('role:admin,editor')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});
// Permission middleware
Route::middleware('permission:users.create')->post('/users', [UserController::class, 'store']);
// Multiple permissions (requires ANY)
Route::middleware('permission:users.create,users.edit')->put('/users/{id}', [UserController::class, 'update']);
// Role OR permission middleware
Route::middleware('role_or_permission:admin,users.create')->get('/users', [UserController::class, 'index']);
// Multiple role_or_permission
Route::middleware('role_or_permission:admin,editor,posts.manage')->group(function () {
Route::post('/manage', [Controller::class, 'handle']);
});
```
### Gate Integration
The package automatically registers Gates for all permissions and roles:
```php
// In controllers
public function store(Request $request)
{
$this->authorize('users.create');
// User can create users
}
// Using Gate facade
use Illuminate\Support\Facades\Gate;
if (Gate::allows('users.create')) {
// Allowed
}
if (Gate::denies('users.delete')) {
abort(403, 'Permission denied');
}
// Check for specific user
if (Gate::forUser($otherUser)->allows('posts.edit')) {
// That user can edit posts
}
// Authorize roles
$this->authorize('administrator');
```
### Blade Directives
Guard provides custom Blade directives for role checking, in addition to Laravel's built-in `@can` directives:
**Custom Role Directives:**
```blade
@role('administrator')
Admin Dashboard
@endrole
@hasrole('editor')
Editor content here
@endhasrole
@hasanyrole(['administrator', 'moderator'])
Content for admins or moderators
@endhasanyrole
@hasallroles(['administrator', 'editor'])
Only for users with BOTH admin AND editor roles
@endhasallroles
```
**Built-in Laravel Directives (via Gate integration):**
```blade
@can('users.create')
Create User
@endcan
@canany(['users.create', 'users.edit'])
You can manage users
@endcanany
@cannot('users.delete')
You cannot delete users
@endcannot
```
### Artisan Commands
**Create a Role:**
```bash
php artisan guard:create-role admin Administrator
# With optional user assignment as the third positional argument
php artisan guard:create-role moderator Moderator 1
```
**Create a Permission:**
```bash
php artisan guard:create-permission users.create "Create Users"
# With optional role assignment as the third positional argument
php artisan guard:create-permission posts.delete "Delete Posts" 1
```
Both commands support Laravel Prompts when optional assignment arguments are omitted.
- `guard:create-role` prompts for an optional user identifier and accepts a user ID, email, or name.
- `guard:create-permission` prompts for an optional role identifier and accepts a role ID or role name.
### Query Scopes
```php
// Users with a specific role
User::query()->withRoles('administrator')->get();
// Users with a specific permission inherited through roles
User::query()->withPermissions('users.create')->get();
// Role scopes
Role::query()->guarded()->get();
Role::query()->unguarded()->get();
// Permission scopes
Permission::query()->wildcard()->get();
Permission::query()->byGroup('users')->get();
```
## ๐ Models Reference
### User Model (via Traits)
**HasRoles trait provides:**
- `roles()` - BelongsToMany relationship
- `assignRole(...$roles)` - Assign one or more roles
- `syncRoles(array $roles, bool $detach = true)` - Sync roles
- `syncRolesWithoutDetaching(array $roles)` - Sync without detaching
- `revokeRole($role)` - Revoke specific role
- `revokeRoles()` - Revoke all roles
- `getRoleNames()` - Get all role names
- `hasRole($role)` - Check single role
- `hasAllRoles(...$roles)` - Check all roles
- `hasAnyRole(...$roles)` - Check any role
**HasPermissions trait provides:**
- `getPermissionNames()` - Get permission names inherited from roles
- `hasPermission($permission)` - Check permission (by name or model)
- `getPermissions()` - Get all permissions inherited from roles
### Role Model
**Properties:**
- `name` (string, unique)
- `label` (string, nullable)
- `description` (text, nullable)
- `is_guarded` (boolean)
**Methods:**
- `getName()` - Get role name
- `isProtectedRole()` - Check if guarded
- `getPermissionNames()` - Get assigned permission names
- `permissions()` - BelongsToMany to permissions
- `users()` - BelongsToMany to users
**Scopes:**
- `guarded()` - Only guarded roles
- `unguarded()` - Only unguarded roles
### Permission Model
**Properties:**
- `name` (string, unique)
- `label` (string, nullable)
- `description` (text, nullable)
- `group` (string, nullable, indexed)
- `is_wildcard` (boolean, auto-set)
**Methods:**
- `getName()` - Get permission name
- `getLabel()` - Get human-readable label
- `getDescription()` - Get description
- `isWildcard()` - Check if wildcard pattern
- `getGroup()` - Get resource group (e.g., 'users')
- `getType()` - Get PermissionType enum
- `roles()` - BelongsToMany to roles
- `giveRoleTo(...$roles)` - Give one or more roles to permission
- `syncRoles(array $roles)` - Sync roles
- `revokeRole($role)` - Revoke role
- `assignRole(...$roles)` - Alias for giveRoleTo
**Scopes:**
- `wildcard()` - Only wildcard permissions
- `byGroup($group)` - Filter by group
## ๐จ Exceptions
```php
use AmdadulHaq\Guard\Exceptions\PermissionDeniedException;
use AmdadulHaq\Guard\Exceptions\RoleDoesNotExistException;
use AmdadulHaq\Guard\Exceptions\PermissionDoesNotExistException;
// Permission denied
throw PermissionDeniedException::create('users.delete');
throw PermissionDeniedException::roleNotAssigned('administrator');
// Role not found
throw RoleDoesNotExistException::named('admin');
throw RoleDoesNotExistException::withId(123);
// Permission not found
throw PermissionDoesNotExistException::named('users.delete');
throw PermissionDoesNotExistException::withId(456);
```
## ๐พ Caching
The package uses intelligent caching:
```php
use AmdadulHaq\Guard\Facades\Guard;
// Clear cache manually
Guard::clearCache();
```
**Cache is automatically cleared when:**
- Roles or permissions are created/updated/deleted
- Role-permission relationships change
**Configuration:**
```php
'cache' => [
'enabled' => true,
'roles_duration' => 3600, // 1 hour
'permissions_duration' => 3600, // 1 hour
],
```
## ๐๏ธ Database Structure
### Roles Table
```php
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('label')->nullable();
$table->text('description')->nullable();
$table->boolean('is_guarded')->default(false);
$table->timestamps();
});
```
### Permissions Table
```php
Schema::create('permissions', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('label')->nullable();
$table->text('description')->nullable();
$table->string('group')->nullable()->index();
$table->boolean('is_wildcard')->default(false);
$table->timestamps();
});
```
### Permission-Role Pivot
```php
Schema::create('permission_role', function (Blueprint $table) {
$table->foreignId('permission_id')->constrained()->cascadeOnDelete();
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->primary(['permission_id', 'role_id']);
});
```
### Role-User Pivot
```php
Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->primary(['role_id', 'user_id']);
});
```
## ๐ข Enums
### PermissionType
```php
use AmdadulHaq\Guard\Enums\PermissionType;
PermissionType::CREATE->label(); // "Create"
PermissionType::READ->label(); // "Read"
PermissionType::WRITE->label(); // "Write"
PermissionType::UPDATE->label(); // "Update"
PermissionType::DELETE->label(); // "Delete"
PermissionType::VIEW_ANY->label(); // "View any"
PermissionType::VIEW->label(); // "View"
PermissionType::RESTORE->label(); // "Restore"
PermissionType::FORCE_DELETE->label(); // "Force delete"
PermissionType::MANAGE->label(); // "Manage"
```
### CacheKey
```php
use AmdadulHaq\Guard\Enums\CacheKey;
CacheKey::PERMISSIONS->value; // 'guard_permissions'
CacheKey::ROLES->value; // 'guard_roles'
```
## ๐ ๏ธ Development
### Code Quality Tools
```bash
# Rector (code refactoring)
composer refactor
composer refactor:check
# Laravel Pint (code style)
composer lint
composer lint:check
# Pest (testing)
composer test
composer test-coverage
# Larastan (static analysis)
composer analyse
```
### Running Tests
```bash
# Run all tests
composer test
# With coverage
composer test-coverage
```
## ๐ง Troubleshooting
### Common Issues
**Issue: `Class 'AmdadulHaq\Guard\Concerns\HasRoles' not found`**
Solution:
```bash
composer dump-autoload
```
**Issue: `Target class [role] does not exist.`**
Solution:
```bash
php artisan config:clear
```
**Issue: Permissions not being recognized**
Solution:
```bash
php artisan cache:clear
# Or
php artisan tinker --execute="\AmdadulHaq\Guard\Facades\Guard::clearCache()"
```
### Performance Tips
1. **Keep caching enabled** in production
2. **Use wildcard permissions** to reduce permission count
3. **Filter at database level** instead of loading all users:
```php
// โ
Good
User::whereHas('roles', fn ($q) => $q->where('name', 'admin'))->get();
// โ Less efficient
User::all()->filter(fn ($u) => $u->hasRole('admin'));
```
4. **Eager load** when needed:
```php
User::with(['roles', 'roles.permissions'])->get();
```
## โ FAQ
**Q: Can I use this with Laravel Sanctum?**
A: Yes! Guard works seamlessly with Sanctum and any auth system.
**Q: Can users have permissions without roles?**
A: No, users receive permissions via roles.
**Q: How do wildcard permissions work?**
A: Create a permission like `posts.*` and it automatically matches `posts.create`, `posts.edit`, etc.
**Q: Can I customize table names?**
A: Yes, publish the config and modify the `tables` section.
**Q: Does it work with multiple guards?**
A: Yes, it integrates with Laravel's authorization system.
**Q: Is there a UI for managing roles?**
A: Guard is backend-only. For a UI, consider Filament Shield or build your own.
**Q: What Blade directives does Guard provide?**
A: Guard ships with `@role`, `@hasrole`, `@hasanyrole`, and `@hasallroles`. Laravel's built-in `@can`, `@canany`, and `@cannot` also work through Gate integration.
**Q: Can permissions be assigned to permissions?**
A: No, permissions are assigned to roles.
## ๐ค Contributing
We welcome contributions! Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
## ๐ Changelog
See [CHANGELOG](CHANGELOG.md) for recent changes.
## ๐ Security
Please review [our security policy](../../security/policy) for reporting vulnerabilities.
## ๐ Credits

## ๐ License
The MIT License (MIT). See [License File](LICENSE.md) for details.
---
Made with โค๏ธ for the Laravel community