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 Role Bundle Laravel Package

ticketpark/dynamic-role-bundle

A Symfony bundle that adds support for dynamic roles, letting applications define and manage security roles at runtime instead of hardcoding them in configuration. Useful when roles need to be created, stored, or changed without redeploying.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. Before writing any code, verify the package is installable in your project at all: the last release is from December 2017, which pins it to the Symfony 2.8/3.x era (AppKernel, services.yml, likely no symfony/flex recipe, possibly PSR-0). On Symfony 4+ you will usually need composer require ticketpark/dynamic-role-bundle --ignore-platform-reqs plus manual bundle registration in config/bundles.php, and even then the service definitions (constructor signatures on Symfony\Component\Security\Core\Role\RoleHierarchy, TokenStorage, EntityManager) will likely break. Budget an afternoon for a compatibility spike before promising anything to a product owner.

Minimal steps once it installs:

  1. composer require ticketpark/dynamic-role-bundle
  2. Register new Ticketpark\DynamicRoleBundle\TicketparkDynamicRoleBundle() in your kernel/bundles file.
  3. Publish/import the bundle config (config/packages/ticketpark_dynamic_role.yaml or app/config/config.yml) — check Resources/config/ in the vendor directory for the actual keys; expect a DBAL connection reference and a table name.
  4. Create the backing table. DBAL persistence means the bundle does not give you Doctrine ORM entities or a schema migration — you get a schema or nothing. Run bin/console doctrine:query:sql "CREATE TABLE ..." or write your own migration mirroring the bundle's expected columns.
  5. Wire your UserInterface implementation to read roles from the bundle's repository/provider instead of (or in addition to) $this->roles.

Where to look first: vendor/ticketpark/dynamic-role-bundle/Resources/config/ for service wiring, then the bundle's DependencyInjection/ directory for the config tree. The README is thin; the DI extension is the documentation.

First use case: an admin UI where a "support" or "regional-manager" role is granted to a user at runtime without a deploy. The whole point is that role assignment is data, not code — security.yaml lists role names as strings, and the DB decides who holds them.

Implementation Patterns

Decorate getRoles(), don't replace it. The idiomatic shape is:

public function getRoles(): array
{
    $roles = $this->staticRoles;          // from your User row
    foreach ($this->dynamicRoleProvider->forUser($this) as $role) {
        $roles[] = $role;                 // or the bundle's role object -> __toString()
    }
    return array_unique($roles);
}

Keep static roles as the source of truth for things that must never disappear due to a DB outage (e.g. ROLE_ADMIN bootstrap accounts), and treat DB roles as additive.

Request-scoped memoization is mandatory. getRoles() is called by the token refresher, every isGranted() check, the profiler, and the security data collector. A naive DBAL read there produces dozens of identical queries per request and will show up immediately in the Symfony profiler's query count. Cache the per-user role set on the object (private ?array $cachedDynamicRoles = null) and, if you need cross-request caching, key a PSR-6 cache on user id with an explicit invalidation hook in your admin grant/revoke service.

Admin grant/revoke service, not raw SQL in a controller. Write one small service around the bundle's repository with grant(UserInterface $u, string $role), revoke(...), rolesFor(UserInterface $u). That gives you a single place to invalidate the cache above and a single place to emit a domain event (audit log of who granted what — you will be asked for this).

DBAL means no Unit of Work. There is no flush(), no dirty checking, no automatic updated_at. If you need auditing columns, use DB-level DEFAULT CURRENT_TIMESTAMP triggers or write them explicitly in your grant service. Correspondingly, wrap multi-row grants in an explicit transaction via the DBAL Connection the bundle is configured with.

Composition with the role hierarchy. Put hierarchy shape in security.yaml (ROLE_MANAGER: [ROLE_SUPPORT]) and keep membership in the DB. That way a hierarchy change is a deploy (reviewed, intentional) while a membership change is a support action. Do not try to make the hierarchy itself dynamic — see gotchas.

Test at the functional level. Because the persistence is raw DBAL, unit tests with mocks prove nothing. Use a KernelTestCase, boot the kernel, seed the roles table, and assert $this->client->getContainer()->get('security.authorization_checker')->isGranted(...). SQLite in-memory works only if the schema SQL is portable; test on the same driver family as production.

Migration tip: if you adopt this, immediately copy the bundle's expected table definition into your own Doctrine migration and stop relying on any doctrine:schema tooling the bundle offers. That decouples you from the abandoned package and makes replacing it later a data-compatible swap.

Gotchas and Tips

  • Role hierarchy is compile-time. security.role_hierarchy is baked into a RoleHierarchy-like service at container build. If you were hoping the DB would also drive hierarchy edges, you will need to decorate the security.role_hierarchy service — and be aware that a decorator doing DB reads runs on every authorization decision. Prefer flat role grants over dynamic hierarchy.

  • Cache invalidation is the real bug factory. On Symfony 4+ the security token and any security.role_hierarchy caching mean a granted role may not take effect until re-login. Document a "roles applied after next login" expectation, or force $tokenStorage->setToken(null) / re-authenticate after a grant. Users will report "I gave them the role and it doesn't work" more than any other issue.

  • Abandonment risk is the headline gotcha. Zero stars, zero dependents, last release 2017. There is no security-response path. Treat the bundle as reference code you vendor, not a dependency: either fork it into src/ or reimplement the ~200 lines of DBAL reads yourself. The value it provides is genuinely small — a table, a repository, a getRoles() integration.

  • License is MIT, so forking is fine — keep the copyright header, and add a README note explaining why you vendored it. Future maintainers will thank you for the paper trail.

  • Debugging dynamic roles: bin/console debug:container security.role_hierarchy and debug:container security.authorization_checker to confirm wiring; doctrine:query:sql "SELECT * FROM <role_table> WHERE ..." to confirm data; the profiler's Security panel to see the actual token roles. If isGranted() fails but the profiler shows the role on the token, the problem is hierarchy or a voter, not the bundle.

  • Extension points to look for: the bundle almost certainly exposes its repository/provider as a public service so you can decorate it. Decoration is the right lever for caching and auditing — it keeps your code out of the bundle's internals and survives a patch bump (not that one is coming).

  • Don't put this on the request hot path for anonymous users. Guard the lookup so it only executes when a token actually has a user; otherwise every 404 page hits the roles table.

  • Verdict for day-to-day use: acceptable as a stopgap for a small, trusted admin surface where you control the table and have a fork in-repo. Not acceptable as a dependency you composer update and forget — the 2017 date means every Symfony minor bump is a coin flip.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator