Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Rbac Laravel Package

huang-yi/laravel-rbac

Laravel RBAC package for assigning roles and permissions to users and protecting routes with middleware. Includes Blade directives and helpers for permission checks, plus commands to manage roles, permissions, and their relationships in your application.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require huang-yi/laravel-rbac

On Laravel 5.5+ the service provider and role / permission middleware aliases are auto-discovered. On older versions register HuangYi\Rbac\RbacServiceProvider in config/app.php and add the aliases in Kernel.php.

Publish and migrate:

php artisan vendor:publish --provider="HuangYi\Rbac\RbacServiceProvider"
php artisan migrate

This gives you a config/rbac.php (where the permission map lives) and the roles / role_user tables. Permissions themselves are not stored in the database — they are declared in config and mapped to role names:

// config/rbac.php
'permissions' => [
    'admin' => ['user.index', 'user.create', 'user.delete'],
    'editor' => ['post.index', 'post.update'],
],

Wire the trait onto your user model:

use HuangYi\Rbac\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}

Seed a role and assign it:

$role = \HuangYi\Rbac\Models\Role::create(['name' => 'admin']);
$user->roles()->attach($role);

First use case: protect an admin-only route group.

Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', AdminController::class)->name('admin.dashboard');
});

Route::middleware(['auth', 'permission:post.update'])->group(function () {
    Route::put('/posts/{post}', [PostController::class, 'update'])->name('post.update');
});

The permission string you check should match the route name (post.update) or whatever naming convention you standardize on — the package does a plain lookup against the config array, so consistency is on you.

Implementation Patterns

Two layers, two mental models. Role checks (role:admin) are coarse-grained gates on whole sections; permission checks (permission:post.update) are fine-grained and belong on individual resource routes. Keep role middleware on route groups and permission middleware on single routes — mixing them on the same route is redundant and makes auditing harder.

Treat config/rbac.php as your permission catalog. Since permissions are code, version them alongside routes. A common pattern is naming permissions after route names so Route::currentRouteName() and the config key always line up:

Route::middleware('permission:'.Route::currentRouteName())

That indirection is handy in a base controller or a macro, but only if your route names are already disciplined.

Blade-level guards. Use the trait directly in views rather than adding new directives:

@if(auth()->user()->hasPermission('user.create'))
    <a href="{{ route('user.create') }}">New user</a>
@endif

The same calls are what the middleware uses internally, so view and route behavior never drift.

Role management screens. Build a small CRUD over Role for admins and sync assignments with $user->roles()->sync($ids). Remember that renaming a role in that UI silently breaks every config key referencing the old name — either lock role names or add a post-rename note to your deploy checklist.

Seed roles deterministically. Don't let roles be created only through the UI. A RoleSeeder with firstOrCreate(['name' => ...]) for each role in config('rbac.permissions') keys keeps local, staging, and production consistent and makes the config the single source of truth:

foreach (array_keys(config('rbac.permissions')) as $name) {
    Role::firstOrCreate(['name' => $name]);
}

Interop with Gates/Policies. The package doesn't replace Laravel's authorization. A pragmatic split: use role: middleware for route-level access, and keep Policies for record-level ownership checks ($user->can('update', $post)). Inside a policy you can consult the trait if you want RBAC to influence record decisions.

Eager-load roles on any page that renders many users or permission-dependent UI to avoid N+1 on the pivot: User::with('roles')->paginate().

Gotchas and Tips

  • Config caching is the sharp edge. In production, permission changes require php artisan config:cache (and a deploy). config:clear locally when edits don't seem to apply. This is the number one source of "I added a permission but it's still 403."
  • Permissions are deploy-time artifacts, not runtime data. If stakeholders need to create permissions from an admin panel, this package is the wrong shape — you'd be bolting a DB-backed permission table onto something designed around a config file. Pick a DB-driven package instead of fighting it.
  • Role names are the join key. The pivot and config both reference roles.name, so a rename cascades into silent failures. Prefer stable, lowercase names (admin, editor) and never rename casually.
  • Middleware ordering matters. Always place auth (or auth:sanctum) before role/permission; otherwise the guard returns a null user and you get an opaque 403 or a null-property error rather than a redirect to login.
  • Permission strings must match exactly. No wildcard matching by default — post.* will not cover post.update unless the package version you pulled supports it (verify against the tag you installed). Copy-paste typos between route names and config are the second most common cause of unexpected 403s.
  • Alias registration on older Laravel. If auto-discovery is off, the middleware aliases must be added to $routeMiddleware manually, or the role:/permission: middleware will throw "class not found."
  • Maintenance status. Last release was mid-2020, so it predates Laravel 9/10/11 conventions. Pin the Laravel version in composer.json and run the suite on your target framework before committing — the trait and model are small enough that a fork or a quick patch is realistic if something breaks.
  • Debugging a 403. Dump auth()->user()->roles to confirm the pivot row exists, then confirm the role name matches a key in config('rbac.permissions'), then confirm the exact permission string. Working outward from the user object localizes the failure in three steps.
  • Extension points are limited by design. The package is thin; if you need per-user (not per-role) permissions, permission groups, or a super-admin bypass, you'll subclass Role, extend the trait, or override the middleware — plan for that rather than expecting a config flag.
  • Cache your own lookups if you go beyond the package. Because the trait resolves permissions through config on each call, heavy loops over hasPermission are cheap compared to queries but still worth avoiding in tight render loops; precompute a $permissions = $user->getPermissions()-style array once per request if you find yourself doing dozens of checks.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console