nadialabs/nadia-menu-bundle
Nadia Menu Bundle is a PHP package for defining, rendering, and managing menus in your application. Build nested navigation trees, control access and active states, and output flexible menu structures with minimal configuration.
First, calibrate expectations: this is a Symfony bundle, not a Laravel package. It extends knplabs/knp-menu-bundle with security-aware checks (role/expression gating on menu items). With 0 stars, 0 dependents, and no ecosystem signal, treat it as read-before-you-run code, not a drop-in dependency. Read src/ and the bundle's DependencyInjection/ extension first — that is the actual documentation.
Minimal path:
composer require nadialabs/nadia-menu-bundle knplabs/knp-menu-bundle symfony/security-bundle
config/bundles.php (Symfony Flex may not auto-register it):
NadiaLabs\NadiaMenuBundle\NadiaMenuBundle::class => ['all' => true],
Confirm the real namespace from composer.json's autoload.psr-4 — do not assume it matches the vendor string.Configuration.php) and any service IDs it defines. That tells you what is configurable (security_check, voter service, expression language) versus hardcoded.$menu->addChild('Admin', ['route' => 'admin_dashboard'])
->setAttribute('security', ['role' => 'ROLE_ADMIN']);
The exact attribute/config key must be verified against the source — the bundle's whole point is translating a per-item security declaration into a KnpMenu voter decision.
If you are on Laravel, stop here. There is no Laravel adapter, no service provider, no facade — you would be porting the security-check logic into your own menu builder. That is usually ~40 lines of code and better done yourself than by pulling in a zero-signal Symfony bundle.
Security-as-a-voter. The intended integration is a KnpMenu VoterInterface implementation that inspects item attributes and returns VoterInterface::ITEM_... decisions. Your controllers/builders stay ignorant of AuthorizationChecker; the menu system filters automatically at render time. Lean on this — do not sprinkle isGranted() calls inside MenuBuilder methods, or you lose the ability to reuse the same tree definition across contexts (e.g. CLI renderers where no token exists).
Declarative item attributes. Standard pattern:
['security' => 'ROLE_X']['security' => "is_granted('EDIT', subject)"]` — requires the ExpressionLanguage service wired through the bundle config.Integration with the renderer. KnpMenu renders the tree it is given. Whether this bundle filters at build time or at render time matters:
Check which one it does before you cache a MenuFactory result in a shared service.
Testing. Build the menu in a kernel test with a mocked/stubbed TokenStorage, assert which children survive. Write a table test: [role, expected visible items]. This is the only reliable way to pin behavior given thin docs — and it protects you if you later fork the bundle.
Extension point to prefer. If the built-in check is too coarse, register your own KnpMenu voter with a higher priority rather than editing vendor code. Custom voters compose; forks rot.
composer require in a Laravel app will pull the bundle but nothing wires it up. Check composer.json's type (symfony-bundle) and require constraints before anything else.composer.lock note; budget a fork.VoterInterface::vote() signatures and the ItemInterface API have churned across KnpMenu majors. Confirm the bundle's require constraint against what you have installed; a silent API mismatch surfaces as an item that is always hidden or always shown.ITEM_ABSTAIN and no other voter decides, KnpMenu defaults to show. If a security check throws (missing expression variable, null token in a CLI/queue context), you may get an unguarded menu rather than a closed one. Decide explicitly whether your policy is allow-list or deny-list, and test the anonymous/no-token case.$menu->getChildren() and print name + getExtra()/attributes after the voter has run. If the bundle exposes a MenuManipulator, run it twice and diff — double-filtering is a common symptom of registering the voter in both bundle config and services.yaml.ROLE_), whether it requires the security.authorization_checker alias explicitly, and whether it registers its voter with a priority. The priority determines whether your own voters get a say before it abstains.src/ under your own namespace, add the tests you need, and drop the composer dependency. For a zero-signal package, owning the 200 lines is lower risk than owning the supply chain.How can I help you explore Laravel packages today?