fawzy / laravel-roles-permissions
A powerful, flexible, and easy-to-use roles and permissions package for Laravel applications with built-in API support
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/fawzy/laravel-roles-permissions
Requires
- php: ^8.1
 - illuminate/database: ^10.0|^11.0
 - illuminate/routing: ^10.0|^11.0
 - illuminate/support: ^10.0|^11.0
 
Requires (Dev)
- mockery/mockery: ^1.5
 - orchestra/testbench: ^8.0|^9.0
 - phpunit/phpunit: ^10.0
 
This package is auto-updated.
Last update: 2025-11-02 13:43:50 UTC
README
A powerful, flexible, and easy-to-use roles and permissions package for Laravel applications with built-in API support.
Features
✨ Core Features
- Multiple roles per user
 - Multiple permissions per user and role
 - Direct user permissions (bypass roles)
 - Role and permission hierarchies
 - Easy-to-use fluent API
 
🔒 Security
- Middleware for route protection
 - Blade directives for view authorization
 - API endpoints for role/permission management
 - Built-in authorization checks
 
⚡ Performance
- Efficient database queries
 - Relationship eager loading
 - Configurable caching support
 
🎨 Developer Experience
- Simple and intuitive syntax
 - Comprehensive documentation
 - RESTful API included
 - Laravel auto-discovery support
 
Table of Contents
Requirements
- PHP 8.1 or higher
 - Laravel 10.x or 11.x
 
Installation
Install the package via Composer:
composer require fawzy/roles-permissions
Publish all package assets (config, migrations, routes, factories, seeders, Postman collection):
php artisan vendor:publish --provider="Fawzy\RolesPermissions\Providers\RolesPermissionsServiceProvider"
Or publish specific assets by tag:
# Publish config only php artisan vendor:publish --tag=roles-permissions-config # Publish migrations only php artisan vendor:publish --tag=roles-permissions-migrations # Publish routes only php artisan vendor:publish --tag=roles-permissions-routes # Publish Postman Collection only php artisan vendor:publish --tag=permissions-postman # Publish factories only php artisan vendor:publish --tag=roles-permissions-factories # Publish seeders only php artisan vendor:publish --tag=roles-permissions-seeders
Run the migrations:
php artisan migrate
Configuration
Add the HasRoles trait to your User model:
use Fawzy\RolesPermissions\Traits\HasRoles; class User extends Authenticatable { use HasRoles; // ... rest of your model }
Usage
Basic Usage
Creating Roles and Permissions
use Fawzy\RolesPermissions\Models\Role; use Fawzy\RolesPermissions\Models\Permission; // Create a role $admin = Role::create([ 'name' => 'Administrator', 'slug' => 'admin', 'description' => 'Full system access' ]); // Create a permission $editPosts = Permission::create([ 'name' => 'Edit Posts', 'slug' => 'edit-posts', 'description' => 'Can edit blog posts' ]);
Roles
Assigning Roles to Users
// Assign single role $user->assignRole('admin'); $user->assignRole($adminRole); // Assign multiple roles $user->assignRole(['admin', 'editor']); // Sync roles (removes all existing roles and adds new ones) $user->syncRoles(['admin', 'editor']); // Remove role $user->removeRole('admin'); $user->removeRole(['admin', 'editor']);
Checking User Roles
// Check if user has role if ($user->hasRole('admin')) { // User is an admin } // Check multiple roles (OR condition) if ($user->hasAnyRole(['admin', 'editor'])) { // User has at least one of these roles } // Check multiple roles (AND condition) if ($user->hasAllRoles(['admin', 'editor'])) { // User has all these roles } // Get all user roles $roles = $user->roles;
Permissions
Assigning Permissions
// Assign permission to role $role->givePermissionTo('edit-posts'); $role->givePermissionTo($permission); // Assign permission directly to user $user->givePermissionTo('edit-posts'); $user->givePermissionTo(['edit-posts', 'delete-posts']); // Sync permissions $user->syncPermissions(['edit-posts', 'delete-posts']); // Revoke permission $role->revokePermissionTo('edit-posts'); $user->revokePermissionTo('delete-posts');
Checking Permissions
// Check if user has permission (checks both direct and role permissions) if ($user->hasPermission('edit-posts')) { // User can edit posts } // Check if user has direct permission (bypasses roles) if ($user->hasDirectPermission('edit-posts')) { // User has direct permission } // Check if user has permission through role if ($user->hasPermissionViaRole('edit-posts')) { // User has permission through a role } // Check multiple permissions (OR condition) if ($user->hasAnyPermission(['edit-posts', 'delete-posts'])) { // User has at least one permission } // Check multiple permissions (AND condition) if ($user->hasAllPermissions(['edit-posts', 'publish-posts'])) { // User has all permissions } // Get all permissions (direct + role permissions) $permissions = $user->getAllPermissions();
Middleware
Protect your routes using middleware:
// Single role Route::get('/admin', function () { return 'Admin Dashboard'; })->middleware('role:admin'); // Multiple roles (OR condition) Route::get('/dashboard', function () { return 'Dashboard'; })->middleware('role:admin|editor|viewer'); // Single permission Route::get('/posts/edit', function () { return 'Edit Posts'; })->middleware('permission:edit-posts'); // Multiple permissions (OR condition) Route::get('/posts', function () { return 'Posts'; })->middleware('permission:edit-posts|view-posts'); // Combine multiple middleware Route::middleware(['auth', 'role:admin', 'permission:edit-posts']) ->group(function () { Route::get('/posts/edit', [PostController::class, 'edit']); });
Blade Directives
Use directives in your Blade templates:
{{-- Check role --}} @role('admin') <a href="/admin">Admin Panel</a> @endrole @hasrole('admin') <button>Admin Action</button> @endhasrole {{-- Check permission --}} @permission('edit-posts') <a href="/posts/edit">Edit Posts</a> @endpermission @haspermission('delete-posts') <button>Delete Post</button> @endhaspermission {{-- Multiple checks --}} @role('admin') @permission('edit-posts') <button>Admin Edit</button> @endpermission @endrole
API Endpoints
The package includes RESTful API endpoints for managing roles and permissions:
Roles
# Get all roles GET /roles-permissions/roles-permissions/roles # Create role POST /roles-permissions/roles-permissions/roles { "name": "Editor", "slug": "editor", "description": "Can edit content" } # Get specific role GET /roles-permissions/roles-permissions/roles/{roleId} # Update role PUT /roles-permissions/roles-permissions/roles/{roleId} { "name": "Senior Editor", "description": "Senior content editor" } # Delete role DELETE /roles-permissions/roles-permissions/roles/{roleId} # Assign permissions to role POST /roles-permissions/roles-permissions/roles/{roleId}/permissions/assign { "permissions": [1, 2, 3] } # Revoke permissions from role POST /roles-permissions/roles-permissions/roles/{roleId}/permissions/revoke { "permissions": [1, 2] }
Permissions
# Get all permissions GET /roles-permissions/roles-permissions/permissions # Create permission POST /roles-permissions/roles-permissions/permissions { "name": "Delete Posts", "slug": "delete-posts", "description": "Can delete blog posts" } # Get specific permission GET /roles-permissions/roles-permissions/permissions/{permissionId} # Update permission PUT /roles-permissions/roles-permissions/permissions/{permissionId} { "name": "Delete All Posts", "description": "Can delete any post" } # Delete permission DELETE /roles-permissions/roles-permissions/permissions/{permissionId}
User Roles & Permissions
# Get user roles GET /roles-permissions/roles-permissions/users/{userId}/roles # Assign roles to user POST /roles-permissions/roles-permissions/users/{userId}/roles/assign { "roles": [1, 2] } # Remove roles from user POST /roles-permissions/roles-permissions/users/{userId}/roles/remove { "roles": [1] } # Sync user roles POST /roles-permissions/roles-permissions/users/{userId}/roles/sync { "roles": [2, 3] } # Check if user has role GET /roles-permissions/roles-permissions/users/{userId}/roles/check/{roleSlug} # Get user permissions GET /roles-permissions/roles-permissions/users/{userId}/permissions # Assign permissions to user POST /roles-permissions/roles-permissions/users/{userId}/permissions/assign { "permissions": [1, 2, 3] } # Revoke permissions from user POST /roles-permissions/roles-permissions/users/{userId}/permissions/revoke { "permissions": [1] } # Check if user has permission GET /roles-permissions/roles-permissions/users/{userId}/permissions/check/{permissionSlug}
API Response Format
All API responses follow this structure:
Success Response:
{
    "success": true,
    "message": "Operation completed successfully",
    "data": {
        // Response data
    }
}
Error Response:
{
    "success": false,
    "message": "Validation failed",
    "errors": {
        "field": ["Error message"]
    }
}
Protecting API Routes
You can protect API routes using middleware:
Route::middleware(['auth:sanctum', 'role:admin'])->group(function () { Route::post('/roles-permissions/roles-permissions/roles', [RoleController::class, 'store']); Route::put('/roles-permissions/roles-permissions/roles/{role}', [RoleController::class, 'update']); Route::delete('/roles-permissions/roles-permissions/roles/{role}', [RoleController::class, 'destroy']); });
Advanced Usage
Custom Role and Permission Models
You can extend the base models:
namespace App\Models; use Fawzy\RolesPermissions\Models\Role as BaseRole; class Role extends BaseRole { // Add your custom methods and properties public function scopeActive($query) { return $query->where('active', true); } }
Update your config file:
// config/roles-permissions.php return [ 'models' => [ 'role' => App\Models\Role::class, 'permission' => App\Models\Permission::class, ], ];
Seeding Roles and Permissions
You can use the built-in seeder or publish it to customize.
Run built-in seeder directly:
php artisan db:seed --class="Fawzy\\RolesPermissions\\Database\\Seeders\\RolesPermissionsSeeder"
Or publish the seeder to your application and customize it:
php artisan vendor:publish --tag=roles-permissions-seeders
Factories are also publishable if you want to tweak how sample data is generated:
php artisan vendor:publish --tag=roles-permissions-factories
Create a seeder:
php artisan make:seeder RolesAndPermissionsSeeder
use Fawzy\RolesPermissions\Models\Role; use Fawzy\RolesPermissions\Models\Permission; class RolesAndPermissionsSeeder extends Seeder { public function run() { // Create permissions $permissions = [ ['name' => 'View Posts', 'slug' => 'view-posts'], ['name' => 'Create Posts', 'slug' => 'create-posts'], ['name' => 'Edit Posts', 'slug' => 'edit-posts'], ['name' => 'Delete Posts', 'slug' => 'delete-posts'], ]; foreach ($permissions as $permission) { Permission::create($permission); } // Create roles $admin = Role::create([ 'name' => 'Administrator', 'slug' => 'admin', 'description' => 'Full access' ]); $editor = Role::create([ 'name' => 'Editor', 'slug' => 'editor', 'description' => 'Can manage content' ]); // Assign permissions to roles $admin->givePermissionTo(Permission::all()); $editor->givePermissionTo(['view-posts', 'edit-posts', 'create-posts']); } }
Using in Controllers
namespace App\Http\Controllers; class PostController extends Controller { public function edit(Post $post) { if (!auth()->user()->hasPermission('edit-posts')) { abort(403, 'Unauthorized action.'); } return view('posts.edit', compact('post')); } public function destroy(Post $post) { $this->authorize('delete', $post); if (auth()->user()->hasPermission('delete-posts')) { $post->delete(); return redirect()->route('posts.index') ->with('success', 'Post deleted successfully'); } abort(403); } }
Using with Laravel Policies
namespace App\Policies; use App\Models\User; use App\Models\Post; class PostPolicy { public function update(User $user, Post $post) { return $user->hasPermission('edit-posts') || $post->user_id === $user->id; } public function delete(User $user, Post $post) { return $user->hasPermission('delete-posts') || ($user->hasRole('editor') && $post->user_id === $user->id); } public function viewAny(User $user) { return $user->hasPermission('view-posts'); } }
Caching Permissions
Enable caching in your config file:
// config/roles-permissions.php return [ 'cache' => [ 'enabled' => true, 'expiration_time' => 60 * 24, // 24 hours in minutes 'key_prefix' => 'roles_permissions.', ], ];
Working with Multiple Guards
If you're using multiple authentication guards:
// Check role for specific guard $admin = auth('admin')->user(); if ($admin->hasRole('super-admin')) { // Admin has super-admin role } // Assign role to admin guard user $admin->assignRole('super-admin');
Querying Users by Role or Permission
// Get all users with a specific role $admins = User::whereHas('roles', function ($query) { $query->where('slug', 'admin'); })->get(); // Get all users with a specific permission $editors = User::whereHas('permissions', function ($query) { $query->where('slug', 'edit-posts'); })->orWhereHas('roles.permissions', function ($query) { $query->where('slug', 'edit-posts'); })->get(); // Get users without a specific role $nonAdmins = User::whereDoesntHave('roles', function ($query) { $query->where('slug', 'admin'); })->get();
Testing
The package includes a test suite. Run tests with:
composer test
Writing Tests
Example test for your application:
namespace Tests\Feature; use Tests\TestCase; use App\Models\User; use Fawzy\RolesPermissions\Models\Role; use Fawzy\RolesPermissions\Models\Permission; class RolesPermissionsTest extends TestCase { public function test_user_can_be_assigned_role() { $user = User::factory()->create(); $role = Role::create(['name' => 'Admin', 'slug' => 'admin']); $user->assignRole('admin'); $this->assertTrue($user->hasRole('admin')); } public function test_user_can_access_protected_route_with_role() { $user = User::factory()->create(); $role = Role::create(['name' => 'Admin', 'slug' => 'admin']); $user->assignRole('admin'); $response = $this->actingAs($user) ->get('/admin'); $response->assertStatus(200); } public function test_user_cannot_access_route_without_permission() { $user = User::factory()->create(); $response = $this->actingAs($user) ->get('/posts/edit'); $response->assertStatus(403); } }
Common Use Cases
E-commerce Application
// Create roles $admin = Role::create(['name' => 'Admin', 'slug' => 'admin']); $seller = Role::create(['name' => 'Seller', 'slug' => 'seller']); $customer = Role::create(['name' => 'Customer', 'slug' => 'customer']); // Create permissions $manageProducts = Permission::create(['name' => 'Manage Products', 'slug' => 'manage-products']); $viewOrders = Permission::create(['name' => 'View Orders', 'slug' => 'view-orders']); $placeOrders = Permission::create(['name' => 'Place Orders', 'slug' => 'place-orders']); // Assign permissions $admin->givePermissionTo(Permission::all()); $seller->givePermissionTo(['manage-products', 'view-orders']); $customer->givePermissionTo('place-orders');
Blog/CMS Application
// Create roles $superAdmin = Role::create(['name' => 'Super Admin', 'slug' => 'super-admin']); $editor = Role::create(['name' => 'Editor', 'slug' => 'editor']); $author = Role::create(['name' => 'Author', 'slug' => 'author']); $contributor = Role::create(['name' => 'Contributor', 'slug' => 'contributor']); // Create permissions $permissions = [ 'publish-posts' => 'Publish Posts', 'edit-posts' => 'Edit Posts', 'delete-posts' => 'Delete Posts', 'create-posts' => 'Create Posts', 'manage-users' => 'Manage Users', ]; foreach ($permissions as $slug => $name) { Permission::create(['name' => $name, 'slug' => $slug]); } // Assign permissions $superAdmin->givePermissionTo(Permission::all()); $editor->givePermissionTo(['publish-posts', 'edit-posts', 'delete-posts', 'create-posts']); $author->givePermissionTo(['create-posts', 'edit-posts']); $contributor->givePermissionTo('create-posts');
Multi-tenant SaaS Application
// Workspace-specific roles $workspaceOwner = Role::create(['name' => 'Workspace Owner', 'slug' => 'workspace-owner']); $workspaceAdmin = Role::create(['name' => 'Workspace Admin', 'slug' => 'workspace-admin']); $workspaceMember = Role::create(['name' => 'Workspace Member', 'slug' => 'workspace-member']); // Workspace permissions $manageWorkspace = Permission::create(['name' => 'Manage Workspace', 'slug' => 'manage-workspace']); $inviteMembers = Permission::create(['name' => 'Invite Members', 'slug' => 'invite-members']); $viewAnalytics = Permission::create(['name' => 'View Analytics', 'slug' => 'view-analytics']); $workspaceOwner->givePermissionTo([$manageWorkspace, $inviteMembers, $viewAnalytics]); $workspaceAdmin->givePermissionTo([$inviteMembers, $viewAnalytics]);
Troubleshooting
Common Issues
Issue: Middleware not working
Make sure middleware is registered. Check app/Http/Kernel.php:
protected $middlewareAliases = [ // ... 'role' => \Fawzy\RolesPermissions\Middleware\RoleMiddleware::class, 'permission' => \Fawzy\RolesPermissions\Middleware\PermissionMiddleware::class, ];
Issue: Trait not found
Ensure you've added the HasRoles trait to your User model:
use Fawzy\RolesPermissions\Traits\HasRoles; class User extends Authenticatable { use HasRoles; }
Issue: Tables not created
Run migrations:
php artisan migrate
If migrations aren't running, publish them first:
php artisan vendor:publish --tag=roles-permissions-migrations php artisan migrate
Issue: API routes not working
Clear route cache:
php artisan route:clear php artisan cache:clear php artisan config:clear
Performance Tips
- 
Eager Load Relationships
$users = User::with(['roles', 'permissions'])->get();
 - 
Use Specific Checks
// Faster - checks direct permissions only if ($user->hasDirectPermission('edit-posts')) { // ... } // Slower - checks both direct and role permissions if ($user->hasPermission('edit-posts')) { // ... }
 - 
Cache Results
$permissions = Cache::remember('user_'.$user->id.'_permissions', 3600, function () use ($user) { return $user->getAllPermissions(); });
 - 
Use Database Indexing
// Add indexes in your migrations $table->index('slug'); $table->index(['user_id', 'role_id']);
 
Security Best Practices
- 
Never trust user input for roles/permissions
// Bad $user->assignRole($request->input('role')); // Good if (in_array($request->input('role'), ['editor', 'author'])) { $user->assignRole($request->input('role')); }
 - 
Always validate in backend
// Don't rely only on frontend checks public function update(Request $request, Post $post) { if (!auth()->user()->hasPermission('edit-posts')) { abort(403); } // Update post }
 - 
Use middleware for routes
Route::middleware(['auth', 'role:admin'])->group(function () { // Protected routes });
 - 
Implement rate limiting for API
Route::middleware(['throttle:60,1'])->group(function () { // API routes });
 
API Authentication
For API endpoints, use Laravel Sanctum or Passport:
Using Laravel Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Update app/Http/Kernel.php:
'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ],
Protect your API routes:
Route::middleware('auth:sanctum')->group(function () { Route::apiResource('roles', RoleController::class); Route::apiResource('permissions', PermissionController::class); });
Example API Usage with Authentication
// Login and get token fetch('/roles-permissions/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: 'user@example.com', password: 'password' }) }) .then(response => response.json()) .then(data => { const token = data.token; // Use token for authenticated requests fetch('/roles-permissions/roles-permissions/roles', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', } }) .then(response => response.json()) .then(roles => console.log(roles)); });
Migration from Other Packages
From Spatie Laravel Permission
The API is similar, making migration straightforward:
// Spatie $user->assignRole('admin'); $user->givePermissionTo('edit posts'); // This package $user->assignRole('admin'); $user->givePermissionTo('edit-posts');
Key differences:
- Permission slugs use kebab-case by default
 - Additional API endpoints included
 - Slightly different method names for some operations
 
Changelog
Version 1.0.0 (2024-10-31)
- Initial release
 - Core roles and permissions functionality
 - Middleware support
 - Blade directives
 - RESTful API endpoints
 - Laravel 10.x and 11.x support
 
Roadmap
- Permission groups/categories
 - Role hierarchy
 - Team/workspace-based permissions
 - GUI for role/permission management
 - Import/Export roles and permissions
 - Permission wildcards (e.g., posts.*)
 - Temporary permissions with expiration
 - Audit logging
 
Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
 - Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
 
Coding Standards
- Follow PSR-12 coding standards
 - Write tests for new features
 - Update documentation as needed
 - Keep backward compatibility in mind
 
Running Tests
composer install
composer test
Support
- Issues: GitHub Issues
 - Discussions: GitHub Discussions
 - Email: 01ahmedfawzy23@gmail.com
 
Credits
- Author: Ahmed Fawzy
 - Contributors: All Contributors
 
License
The MIT License (MIT). Please see License File for more information.
Quick Links
Made with ❤️ for the Laravel community