laravel-doctrine/acl
Provides ACL support for Laravel Doctrine, letting you define roles and permissions as Doctrine entities and check access to resources through a Laravel-friendly API. Useful for adding role-based authorization to Doctrine-backed Laravel apps.
laravel-doctrine/acl plugs Doctrine's ACL component into Laravel's authorization Gate, so $user->can('post.edit'), Gate::allows(...), @can in Blade and $this->authorize(...) in controllers all resolve through ACL entries stored in the database instead of hand-written policies. It sits on top of laravel-doctrine/orm, so you need the ORM package configured with an entity manager first.
composer require laravel-doctrine/acl — as of 2.1.0 the constraint tracks Laravel 13 (see Gotchas if you're on an older major).php artisan vendor:publish and pick the ACL provider/tag) and inspect config/acl.php. This is the file that matters most: it maps your User entity, the Role/Permission entities, the ACL table names and the default security identity.User entity (HasRoles, HasPermissions, or the combined permissible trait) and make sure the entity implements the package's security-identity contract.acl_classes, acl_entries, acl_object_identities, acl_object_identity_ancestors, acl_security_identities) either through the Doctrine schema tool or published migrations — nothing works until these exist.Gate::allows('post.edit') from a controller or @can('post.edit') in a view.Look first at config/acl.php and the package's contracts directory; almost every "it returns false" problem is a mapping in that config or a missing ACL table row.
Traits over custom policies. The usual workflow is to give User the role/permission traits and attach the permissible trait to entities you want to protect. Once wired, you stop writing per-action policy classes for CRUD permissions and instead grant post.edit / post.delete strings to roles at seed or admin time.
Gate as the single entry point. Because the package registers a Gate adapter backed by Doctrine ACL, all the Laravel ergonomics still apply: Gate::inspect() for denial messages, Gate::before() for super-admin bypass (fn ($user) => $user->hasRole('super-admin') ?: null), can middleware on routes, and @can/@cannot in Blade.
Seeding and admin UI. Manage grants through the ACL manager the service provider binds into the container rather than editing rows by hand. A typical pattern is a RoleSeeder that creates role entities and inserts object identities once, plus an admin screen that only ever inserts/deletes acl_entries for existing identities.
Policies as thin delegators. For anything with business rules beyond a flat permission, keep a Laravel policy and have it call the ACL check internally (return $user->can('post.edit', $post) || $post->isDraft()), so authorization logic stays discoverable via php artisan inspection but still benefits from centrally-managed grants.
Organisation / multi-tenant scoping. The package's organisation contract lets you scope roles to a tenant. Resolve the current organisation in a middleware and inject it when checking permissions rather than baking tenant IDs into permission slugs.
Long-running processes. In Octane/queued workers, ACL decisions can be cached in memory. Flush the ACL cache when a grant or revoke is written, or wire the Doctrine ACL cache to a shared store, otherwise a worker keeps serving stale "allowed" results.
laravel-doctrine/orm and PHP, since Laravel 13 raises the PHP floor.false. Log denials with Gate::inspect()->message() during development.acl.php names an entity class that has been renamed or namespaced differently, you get confusing "class not found"-style errors or permanent denials. Re-run the config publish after upgrades and diff it.can() call, not at boot.acl_entries and acl_object_identities directly to confirm what was actually written; a grant that "did nothing" is usually a failed flush or a transaction rollback in the seeder.Role/Permission contracts on your own entities if you need extra columns, and override the entity mappings in config/acl.php rather than extending the shipped entities.How can I help you explore Laravel packages today?