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

Dynamic Acl Laravel Package

iya30n/dynamic-acl

A Laravel package for dynamic, database-backed role and permission management. Define roles and permissions at runtime, assign them to users, and protect routes or views with middleware and Blade directives without hardcoding gates.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish first, integrate second. The package is small and pre-1.0 in spirit, so treat the vendor directory as the source of truth — read it before trusting any README.

composer require iya30n/dynamic-acl
php artisan vendor:publish --provider="Iya30n\DynamicAcl\DynamicAclServiceProvider"
php artisan migrate
  1. Check the service provider at vendor/iya30n/dynamic-acl/src/ immediately. Confirm: the publish tags it registers, whether migrations are auto-loaded, and the exact trait name (HasAcl, HasRoles, or similar). This takes two minutes and saves an hour of guessing.
  2. Publish config and migrations if tags exist; otherwise migrations may run from the package path directly. Skim the migration for the pivot table names (role_user, permission_role, etc.) since you'll query them when debugging.
  3. Wire the trait onto your User model:
    use Iya30n\DynamicAcl\Traits\HasAcl;
    
    class User extends Authenticatable
    {
        use HasAcl;
    }
    
  4. Seed the baseline. Create roles (admin, editor, viewer) and attach permissions in a seeder. Do this before writing any authorization logic — an empty ACL table makes every can() return false, which looks like a broken install.
  5. First use case: gate a single route with the package's middleware or a Gate::before super-admin bypass, then verify with php artisan tinker:
    User::first()->hasPermission('posts.edit'); // confirm method name from source
    

The first meaningful win is replacing hardcoded $user->role === 'admin' checks with permission checks that a non-developer can later reconfigure from a UI or seeder.

Implementation Patterns

Permission naming: use resource.action. posts.create, posts.edit, billing.view. Flat, dot-delimited names survive being rendered in checkbox lists and are trivial to glob-match if the package supports wildcards. Avoid sentence-style names and avoid encoding hierarchy (posts.edit.own) unless the package's matching logic explicitly supports it.

Role seeding as a repeatable artifact, not a one-off.

// database/seeders/AclSeeder.php
$admin = Role::updateOrCreate(['name' => 'admin']);
$admin->syncPermissions(Permission::pluck('id')); // verify sync method exists

Use updateOrCreate + sync so the seeder is idempotent and re-runnable in CI and on deploy.

Enforce at all three layers, not just one.

  • Route layer — middleware alias on the route group: ->middleware('can:posts.edit') or the package's own middleware if it provides one.
  • Controller/policy layer — $this->authorize('update', $post) for object-level rules layered on top of the coarse permission check.
  • View layer — @can('posts.edit') around action buttons. Never rely on hidden buttons for security; it's UX only.

Wrap permission checks in a thin policy or service. If the package's API is thin or its method names shift, you want one place to change:

class PostPolicy
{
    public function update(User $user, Post $post): bool
    {
        return $user->hasPermission('posts.edit')
            && ($user->id === $post->user_id || $user->hasRole('admin'));
    }
}

This also gives you a seam to swap the package out later without touching controllers.

Dynamic management means a UI surface. The "dynamic" premise is that roles and permissions are data, not code. Plan for an admin screen or Artisan command from the start:

php artisan acl:grant admin posts.publish

Build the command early — it doubles as your test fixture factory and your emergency hotfix tool in production.

Cache aggressively, invalidate precisely. Permission lookups run on nearly every request. Cache the user's resolved permission set (e.g. Cache::remember("acl:{$user->id}", 3600, fn() => ...)) and forget it in whatever mutation path assigns roles or permissions. If the package ships its own cache layer, confirm the invalidation hooks actually fire on role/permission attach and detach.

Gotchas and Tips

The package is stale — verify framework compatibility before committing. Last release June 2023 means it predates recent Laravel majors. Check composer.json constraints for laravel/framework and php. If it won't install on your version, you have three options in order of preference: (1) fork and widen the constraints, (2) use spatie/laravel-permission for far better maintenance, (3) pin an older Laravel. Do not silently use --ignore-platform-reqs in production.

Empty ACL = everything is denied. This is the single most common "it's broken" report. When a can() returns false unexpectedly, run through this checklist in order:

$user->roles;                       // roles attached at all?
$user->roles->flatMap->permissions; // permissions on those roles?
config('dynamic-acl');              // is the super-admin bypass configured?

Then check Gate::before — if you registered a bypass, it can mask real failures during development.

Guard mismatch is silent. If your users authenticate on web but roles are seeded against a different guard, permission checks simply fail with no error. Keep a guard column or config aligned with your auth guards and assert it in a test.

Cache staleness after role changes. The most frequent production bug in ACL systems: an admin revokes a permission, but the affected user stays privileged until the TTL expires. Write a test that assigns a role, checks access, revokes, and checks again in the same process. If it fails, your invalidation is incomplete.

N+1 on permission checks. Checking permissions in a Blade loop over 50 posts will re-query roles and permissions 50 times. Eager load ($user->load('roles.permissions')) or lean on the cache layer. Watch the query log while loading the index page — this is where ACL packages quietly destroy performance.

Publish the migrations, don't rely on the package's. If the vendor migrations auto-run, later package upgrades can conflict with your applied schema. Copy them into database/migrations, take ownership, and add your own indexes on role_id/permission_id — small packages often omit them.

Extension points to check before forking: the config file (usually lets you override the Role, Permission, and User models) and any contract/interface bindings in the service provider. Overriding model classes via config is far cheaper than a fork when you need extra columns like label or description for your admin UI.

Budget for the exit. With 12 stars and a single 2023 release, bus factor is the real risk. Keep authorization logic behind policies and a small Acl service wrapper so migrating to spatie/laravel-permission is a one-file change plus a data migration — not a rewrite scattered across controllers. If this is going into anything long-lived, make that seam the first thing you build.

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
shitoudev/permissionmanager