igdr/config-knp-menu-bundle
A Symfony bundle that adds configuration-driven menu definitions on top of KnpMenuBundle, letting you declare menus and items in config files instead of writing PHP menu builders by hand.
This is a Symfony bundle, not a Laravel package. Before anything else, confirm that your project is Symfony (or that you're willing to run Symfony components inside Laravel via a bridge) — knp-menu and knp-menu-bundle are Symfony-native, and this package sits on top of them as a config loader. If you're on Laravel, the honest answer is that you'd be reimplementing what spatie/menu or a simple config-driven Menu class gives you for far less risk.
Assuming Symfony:
composer require knplabs/knp-menu-bundle
composer require igdr/config-knp-menu-bundle
config/bundles.php (or app/AppKernel.php on older Symfony):
Igdr\ConfigKnpMenuBundle\IgdrConfigKnpMenuBundle::class => ['all' => true],
bin/console cache:clear
bin/console debug:container --tag=knp_menu.menu_builder
bin/console debug:config igdr_config_knp_menu
Where to look first: the bundle's DependencyInjection/ extension and its Resources/config/ directory. That tells you the exact YAML root key, file location convention, and which services get registered. Given the package has essentially no documentation and zero dependents, reading the DI extension is faster than guessing.
First use case: a top-level navigation menu whose items can be contributed by multiple bundles. Instead of every bundle registering a MenuBuilder service in PHP, each bundle drops a small YAML file and the bundle merges them into one KnpMenu tree — exactly the "configure across bundles" promise in the description. That's the one scenario where this package beats writing builders by hand, so validate it against that before adopting anything else.
Per-bundle YAML contribution with a single merged tree. The core pattern is one config file per bundle, all loaded and merged at container compile time. Treat the app-level config as the "last word" layer and bundle configs as defaults. Ordering matters (see Gotchas).
Static menus for public navigation, dynamic bits as children. Define the stable skeleton in YAML (labels, routes, ordering) and attach dynamic entries — current user's name, unread count, admin-only links — via a small menu builder or a knp_menu.menu_builder service that decorates the YAML-defined menu. This keeps the fast-changing stuff out of config and avoids cache invalidation on every user-specific change.
Route-driven items with attribute bags. The natural mapping is label + route + routeParameters + attributes (classes, icons, data-*). Push as much as possible into attributes rather than inventing custom keys — KnpMenu's ItemInterface already exposes setAttribute/getAttribute, and the Twig renderer reads them directly via knp_menu_render.
Security-aware visibility via voters. Don't gate items with if logic in YAML. Register a custom knp_menu.voter and let items carry roles attributes; KnpMenu's VoterInterface will hide them at render time. This is the clean way to get "admin menu vs public menu" from one config without duplicating files.
Reusable menu templates. Pair the YAML with a Twig override of KnpMenuBundle::menu.html.twig (or use knp_menu_render(menu, {'template': ...})) so attribute bags map to your design system's markup. This is where most of the actual integration work lives.
Locale-scoped trees. If the YAML loader supports translation domains (check the extension), translation_domain on items is a low-effort win — otherwise bake the |trans into your Twig template and use abstract keys like menu.nav.home as labels.
Testing. Build the menu in a kernel test and assert on the item tree (getChildren, getUri, getLabel) rather than rendering HTML. Config-driven trees are easy to regression-test this way, and it's the cheapest safety net you'll have for a package with no test suite of its own you can trust.
bundles.php) and the bundle's internal config loading determine which YAML wins on key collisions. If an override "doesn't apply," it's almost always ordering, not syntax. Verify with bin/console debug:config.cache:clear in dev and a deploy in prod. This bites people who expect hot reload like a normal config file read at runtime.Configuration tree builders and service definitions). If it's stale, fork it — the codebase is small enough to vendor.composer.json and note it in your upgrade checklist; an abandoned transitive dependency here will block a Symfony major upgrade.Menu value object resolved from config/menu.php, with a Blade component for rendering. That's an afternoon of work and zero dependency risk.bin/console lint:yaml config/, bin/console lint:container, bin/console debug:container --tag=knp_menu.menu_builder, then debug:config. Ninety percent of issues are a mistyped root key (silently ignored) or a missing bundle registration.MenuBuilderEvent (knp_menu.menu_builder events) and VoterInterface are stable, documented, upstream APIs — hook your customizations there rather than forking this bundle. That way, if igdr/config-knp-menu-bundle goes unmaintained, ripping it out later means deleting YAML, not untangling custom code.when: clause, move that item to a builder service — YAML debugging is miserable and the bundle gives you no validation to help.How can I help you explore Laravel packages today?