integrated/menu-bundle
A Laravel bundle for defining, organizing, and rendering application menus, giving you a structured way to manage navigation items and integrate menus cleanly into your PHP project.
integrated/menu-bundle is a Symfony bundle, not a Laravel package — so treat your Laravel instincts (service providers, artisan, config/*.php) as inapplicable until proven otherwise. It does two distinct things, and you should decide which you actually need before installing:
MenuEvents so modules can append navigation items programmatically (the admin-side composability seam).website-bundle it lets non-engineers edit front-end navigation.backend-bundle, usually website-bundle).composer.json. Given near-zero adoption and unclear maintenance cadence, read the constraints before installing rather than assuming current Symfony support.composer require integrated/menu-bundle
Symfony Flex / 4+:
// config/bundles.php
return [
Integrated\Bundle\MenuBundle\IntegratedMenuBundle::class => ['all' => true],
];
Symfony 3.x:
// app/AppKernel.php
$bundles[] = new Integrated\Bundle\MenuBundle\IntegratedMenuBundle();
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
# local experimentation only
bin/console doctrine:schema:update --force
The exact key is version-dependent; typically a bundle config block under integrated_menu:
# config/packages/integrated_menu.yaml
integrated_menu:
provider: database
If the bundle auto-wires a provider chain (database + event dispatcher), you may not need to set this at all — but verify by dumping the container, don't assume.
vendor/integrated/menu-bundle/src/) — this is where N+1 queries, caching, and tree traversal behavior live.MenuEvents constants / event payload — this is the public contract you will code against.Don't start with primary navigation. Stand up the DB provider behind a flag and render a single low-risk menu (footer or secondary nav). This validates registration, event wiring, and caching before you risk the main nav.
The strongest reason to use this package inside the suite. Instead of every module editing one central menu array (a merge-conflict hotspot), each module subscribes and contributes its own items:
// In a module's event subscriber
public function onMenuBuild(MenuEvent $event): void
{
$event->addItem('reports', [
'label' => 'Reports',
'route' => 'reports_index',
'parent' => 'admin',
]);
}
Register the subscriber like any Symfony listener (services.yaml with kernel.event_subscriber or a tagged kernel.event_listener bound to the MenuEvents constant). Keep each contributor idempotent — assume the builder may be called more than once per request in some flows.
This is the pattern that justifies the package when marketing/ops own the site:
website-bundle reads via the provider and renders the front end.Wrap the read in a thin internal service (e.g. NavRepository::tree($location)) so a future switch to spatie/laravel-menu or a hand-rolled menus/menu_items pair is a swap, not a rewrite.
Menu rendering happens on every page, so the provider read is your hot path. Cache the assembled tree (not individual rows) keyed by location/locale, and invalidate on menu-edit events:
// pseudo-subscriber on the edit/save event
public function onMenuSaved(MenuSavedEvent $e): void
{
$this->cache->delete($this->navRepository->cacheKey($e->getLocation()));
}
Invalidate on all write paths — create, update, delete, and reorder. Reordering is the one everyone forgets and it's exactly what breaks stale-nav bugs in production.
Use the package's active-state detection (route/URL match) at the item level, and layer authorization on top: filter the tree server-side so users only receive items they can reach, rather than hiding links in CSS. A hidden link is not a permission check.
website-bundle for the front end — this is the documented, glue-free path.integrated/menu-bundle and its Integrated siblings together and record a tested matrix (PHP version, Symfony version, DB driver).This is Symfony, not Laravel. The "bundle" vocabulary is the tell. Don't plan for artisan, service providers, or Laravel-style config unless you've verified a bridge exists. The package supplies a provider, not an admin UI — budget for building or sourcing the editing surface separately.
Zero adoption = you are the maintainer. Zero stars, zero dependents, no external docs, no battle-tested bug reports. Assume bus factor of one and unclear release cadence. The lowest-drama posture is to vendor/fork a pinned copy and treat upstream as reference, with a documented diff.
Schema ownership is yours. Run doctrine:migrations:diff early and inspect the generated migration — verify the tree model actually supports depth, ordering, and locale, plus whether items can be permission-scoped. If it only models flat rows with a parent ID, nesting depth and reorder semantics are on you to enforce.
N+1 on nested trees. The classic failure: one query per level or per child when assembling the tree. Check the provider source for eager loading / single-query assembly before shipping, and cache the assembled tree rather than the raw rows.
Fail soft when a listener throws. An exception in any MenuEvents subscriber can break rendering for the whole page. Isolate listeners, and fall back to a cached or static menu if the event/dispatch path fails. On-call should be able to disable the DB provider without taking navigation down — write that runbook.
Stale cache after edits is the #1 user-visible bug in DB-driven nav. Confirm invalidation covers every write path, including reorder and bulk operations. Watch for cache stampedes right after an editor saves — consider a short lock or stale-while-revalidate.
Validate and sanitize at both ends. Authorize menu items at write time (who may edit the tree) and sanitize labels/URLs at render time. Orphaned or unauthorized items reaching production is a real risk in an editor-owned system.
Compatibility is unproven until you test it. Confirm PHP/Symfony versions, MySQL/Postgres migration portability, and that reads don't assume a specific cache backend. If the package is stale against your framework version, price the compatibility patch into the spike rather than discovering it mid-integration.
Extension points to know:
MenuEvents constants — the contribution contract; version the payload if you extend it.Fallback plan, stated plainly: if you're not on Integrated, spatie/laravel-menu or a simple menus/menu_items pair is comparable effort with far more support behind it. Adopt this one because it removes glue work inside the suite — not as a standalone navigation system.
How can I help you explore Laravel packages today?