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 Roles Laravel Package

jeremykenedy/laravel-roles

jeremykenedy/laravel-roles is a comprehensive Laravel package for managing user roles and permissions. It provides migrations, models, middleware, Blade directives, and an optional admin interface to create roles, assign permissions, and protect routes with ease.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and let the installer do the heavy lifting:

composer require jeremykenedy/laravel-roles
php artisan roles:install --css=bootstrap5

roles:install publishes config/roles.php, the migrations, the view set and (interactively or via --css) picks the CSS framework. Run php artisan migrate afterwards, then add the HasRoles trait to your User model so the package can attach roles and permissions to users.

The first place to look is the new docs/ folder in the package — it now covers installation, configuration, roles, permissions, levels and inheritance, blade and middleware, the GUI, artisan commands, the API, seeding, testing and upgrading. Read docs/upgrading.md before touching an existing install; the API middleware change in v13 is the one that will bite.

First meaningful use case: seed your default roles/permissions (docs/seeding.md), then protect a route group with the role middleware and gate a nav item with a Blade directive:

Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', AdminController::class);
});
@role('admin')
    <a href="{{ route('admin') }}">Admin</a>
@endrole

That covers 80% of day-to-day usage.

Implementation Patterns

Middleware on route groups. The package registers role, permission and level middleware. Use role:admin for coarse gating, permission:edit-posts for capability checks, and level:>=5 (or the equivalent comparator syntax documented in docs/middleware.md) for hierarchical checks. Prefer route-group-level application over per-controller checks so the policy is visible in routes/web.php.

Blade directives (@role, @permission, @level, plus their @else variants) keep view logic declarative. Use them for navigation and action buttons — never as the only authorization, always pair with middleware or a policy.

Levels and inheritance. Roles carry a numeric level and can inherit permissions from lower-level roles. This is the package's distinguishing feature versus simpler role packages; model your hierarchy once (e.g. user:1, editor:50, admin:100) and let level middleware do the work rather than enumerating roles per route.

The GUI is for administrators, not end users. Ship it behind role:admin (or your equivalent) and be deliberate about the ROLES_CSS_FRAMEWORK value — it must match the framework your admin layout already loads, otherwise you get two competing icon/utility sets on the page.

The JSON API is the integration point for SPAs and mobile clients. In v13 its middleware is finally config-driven via rolesAPIAuthEnabled, rolesAPIMiddlewareEnabled and rolesAPIMiddleware (default role:admin). Verify your config explicitly rather than relying on defaults — see Gotchas.

Seeding default roles/permissions from a seeder keeps environments reproducible; the docs cover the idempotency pattern, which matters if you re-run db:seed on live.

Bulk operations. Bulk restore/destroy now run in a transaction and report outcomes. If you were previously wrapping controller calls in your own DB::transaction() defensively, you can drop that — but check the return payload, since silent failures are gone and your UI may now receive an error shape it hasn't handled.

Gotchas and Tips

The v13 API middleware change is a security fix, not a nicety. Before v13 the JSON API hardcoded auth:api, so with ROLES_API_ENABLED=true any authenticated user could read every role, permission and user, and create roles. The new code honours rolesAPIMiddleware (default role:admin). If you set ROLES_API_MIDDLEWARE_ENABLED=false to restore the old behaviour, you are reintroducing that hole — only do it if you have your own authorization layer in front. Audit any install where the API was enabled and the admin UI was the only thing you thought was protected.

Denials are now 403, not 500. If you had exception handling tuned to the old stack-trace 500 (custom Handler::render, log-based alerting, or tests asserting status 500), update them. The role/permission/level middleware now throws an exception carrying a 403 status.

Laravel 11 is still declared supported but no longer tested. Every 11.x release carries an advisory with no patched 11.x, so composer will refuse to resolve it under the default policy. If you're pinned to L11, expect to configure audit/allow-plugins or upgrade. The claim "Laravel 5.3 through 13" is wider than the tested matrix (PHP 8.2–8.4 on Laravel 12 and 13) — treat older combinations as best-effort.

Config caching will hide your middleware choice. Because the API middleware is now read from config/roles.php, changing ROLES_API_MIDDLEWARE_ENABLED or ROLES_API_MIDDLEWARE in .env and seeing no effect almost always means a stale config cache. php artisan config:clear (or config:cache in deploy) after any change.

Only one view set is active. ROLES_CSS_FRAMEWORK selects bootstrap4 (default), bootstrap5 or tailwind; icons ship with each (FA4, FA6, or inline SVG — Tailwind loads neither). Changing it on an existing install is what roles:update / roles:switch --css=tailwind are for. Don't hand-edit assets; re-run the command so the publish step stays consistent.

Escaping changed. Role and permission names are now escaped in the GUI, along with flash and validation output. The old behaviour was raw — meaning anyone who could name a role could store markup that executed in an admin's session. If you have custom published views that relied on the unescaped output, re-audit them; if you were unaffected, upgrade is safe. This is a genuine stored-XSS fix, so prioritise it.

getPermissions() is now N+1-safe only when the relation is already loaded. Eager-load where you can (User::with('roles.permissions')) to actually get the benefit; calling it lazily per user in a loop is still a query per user.

PHP 7.2 floor. The package still declares PHP 7.2+ and a test now guards it. If you publish and extend the models, avoid typed properties (7.4+) and other syntax above the floor unless you've deliberately raised your own requirement.

Testing. With the docs rewritten there is now a testing section. In practice: RefreshDatabase, seed via the package seeder (not your app seeder) in setUp, and assert middleware with actingAs($user)->get(...)->assertForbidden() rather than asserting exceptions — the 403 change makes the HTTP-level assertion both simpler and more robust.

Extension points. Publish the config (vendor:publish --tag=roles-config or via roles:install) rather than editing inside vendor/. Custom models, views and translations are the documented override surface; keep your customisations in your app, not in the package tree, so composer update stays clean.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource