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

Entrust Laravel Package

savannabits/entrust

Savannabits/Entrust adds role-based access control to Laravel, letting you assign roles and permissions to users via Eloquent traits, middleware, and Blade directives for simple authorization checks.

View on GitHub
Deep Wiki
Context7

Getting Started

v9.0.0 is a compatibility release: it adds Laravel 10 and 11 support (and, by implication, drops the older framework lines that v8.x targeted). If you are on Laravel 10/11 this is the version you want; if you are still on Laravel 8/9, stay on ^8.1 and plan the framework upgrade first.

Minimal setup:

composer require savannabits/entrust:^9.0
php artisan vendor:publish   # pick the Entrust provider/tags for config + migrations
php artisan migrate

Publish both the config (config/entrust.php) and the migrations — the four tables (roles, permissions, role_user, permission_role) are not shipped pre-migrated, and the config is where you override table names and the Role/Permission model classes.

Wire the traits onto your models — this is the step people forget:

// app/Models/User.php
use Zizaco\Entrust\Traits\EntrustUserTrait;   // verify namespace in the fork's README
class User extends Authenticatable { use EntrustUserTrait; }

// app/Models/Role.php
use Zizaco\Entrust\Traits\EntrustRoleTrait;
class Role extends Model { use EntrustRoleTrait; protected $fillable = ['name','display_name','description']; }

// app/Models/Permission.php
use Zizaco\Entrust\Traits\EntrustPermissionTrait;
class Permission extends Model { use EntrustPermissionTrait; protected $fillable = ['name','display_name','description']; }

First use, in tinker or a seeder:

$admin = Role::create(['name' => 'admin', 'display_name' => 'Administrator']);
$edit  = Permission::create(['name' => 'post.edit', 'display_name' => 'Edit posts']);
$admin->attachPermission($edit);

$user->attachRole('admin');       // accepts a Role instance or name
$user->hasRole('admin');          // true
$user->can('post.edit');          // via the package's permission check

Look first at the published config/entrust.php and the migration file — they define the contract everything else depends on (model classes, table names, pivot columns).

Implementation Patterns

Seed the whole matrix, then assign. Roles and permissions are database rows, not code. A dedicated seeder that idempotently firstOrCreates every role and permission and attaches the mapping is the single highest-value thing you can build; ad-hoc attachRole calls scattered through controllers rot quickly.

Route protection via the bundled middleware. The package ships role/permission/ability middleware. In Laravel 10 you alias them in app/Http/Kernel.php; in Laravel 11 there is no Kernel, so register aliases in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role'       => \Zizaco\Entrust\Middleware\EntrustRole::class,
        'permission' => \Zizaco\Entrust\Middleware\EntrustPermission::class,
        'ability'    => \Zizaco\Entrust\Middleware\EntrustAbility::class,
    ]);
})

Then:

Route::middleware('role:admin')->group(...);
Route::middleware('permission:post.edit')->group(...);
Route::middleware('ability:admin|owner,post.edit')->group(...);  // roles | role, then permission

Blade directives are registered by the service provider and are the cleanest place for UI-level checks:

@role('admin') <a href="/admin">Admin</a> @endrole
@permission('post.edit') <button>Edit</button> @endpermission
@ability('admin|owner', 'post.edit') ... @endability

Fine-grained authorization belongs in policies, not Entrust middleware. Entrust answers "does this user hold this role/permission globally." It cannot answer "does this user own this post." Compose the two: use a Policy/FormRequest for resource-level checks and call $user->hasRole()/$user->can() inside it.

Super-admin bypass. Register a Gate::before in a service provider that returns true for your super-admin role; otherwise you end up attaching every permission to the admin role forever.

Custom fields. Because Role/Permission models are swappable in config, you can add display_name, description, or a level column and point the config at your own subclasses. Do this before writing admin CRUD.

Gotchas and Tips

  • v9 is a hard version floor. The changelog is only "Support Laravel 10 and 11," which means the constraint moved to laravel/framework ^10|^11 and PHP 8.1+. Composer will refuse the upgrade on older apps — that's the breaking change, and it's a framework-upgrade blocker, not something you can shim.
  • The can() collision with Laravel's Gate. Entrust's permission check and Illuminate\Auth\Access\Authorizable::can() overlap. Do not assume $user->can('post.edit') and Gate::allows('post.edit') are the same code path — Entrust permissions are not automatically registered as Gate abilities. Pick one style per codebase, and if you need both, register Entrust permissions into the Gate explicitly.
  • Cached roles on the user instance. Entrust memoizes roles/permissions on the model. If you attachRole() and then check hasRole() on the same instance in the same request, you may get a stale answer. Re-fetch the user ($user->refresh() / re-query) after mutating roles, and reload in tests between assertions.
  • No cascade on the pivots. The stock migrations create the pivot tables without foreign keys, so deleting a Role or User leaves orphaned role_user / permission_role rows. Add onDelete('cascade') in your own migration or clean up in a model/observer deleting hook.
  • Config caching. Any change to config/entrust.php (table names, model classes) requires php artisan config:cache (or a clear) to take effect in a cached production deploy — a classic "works locally, wrong table in prod" bug.
  • Middleware in Laravel 11. Do not copy a Laravel 10 Kernel.php snippet into an L11 app. If the service provider does not auto-alias the middleware, manual registration in bootstrap/app.php is mandatory or route middleware silently 500s on "target class [role] does not exist."
  • No admin UI, no guard/team scoping. Unlike spatie/laravel-permission, Entrust ships no UI, no multi-guard support, and no teams. Build your own CRUD, and validate/sanitize role names heavily — role assignment is a privilege-escalation surface, so guard the controller that calls attachRole with its own authorization check.
  • Extension points: model swapping via config, Entrust::hasRole() / Entrust::can() facade calls for non-$user contexts, and the ability middleware for compound checks. Everything else you need, you write.
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