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

Menu Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. Dispatches MenuEvents so modules can append navigation items programmatically (the admin-side composability seam).
  2. Ships a database menu provider so menu items live in Doctrine and can be edited from the Integrated backend — the interesting case, because paired with website-bundle it lets non-engineers edit front-end navigation.

Prerequisites

  • A working Symfony app already on the Integrated suite (backend-bundle, usually website-bundle).
  • Doctrine ORM configured — the DB provider persists menu items.
  • A Symfony version compatible with the bundle's composer.json. Given near-zero adoption and unclear maintenance cadence, read the constraints before installing rather than assuming current Symfony support.

Install and register

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();

Create the schema

bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate

# local experimentation only
bin/console doctrine:schema:update --force

Point the provider at the database

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.

Where to look first

  • The provider class (find it in vendor/integrated/menu-bundle/src/) — this is where N+1 queries, caching, and tree traversal behavior live.
  • The MenuEvents constants / event payload — this is the public contract you will code against.
  • The Doctrine entity/mapping for menu items — this is the schema you now own.

First use case

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.

Implementation Patterns

1. Contribute admin nav items via events (the composability seam)

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.

2. DB-backed front-end menus (editor-owned navigation)

This is the pattern that justifies the package when marketing/ops own the site:

  • Seed menu items via a data fixture or migration for initial content.
  • Editors modify the tree in the Integrated backend.
  • 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.

3. Cache at the provider boundary

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.

4. Active-state / permission gating

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.

5. Integrate with the rest of the suite, not beside it

  • Pair with website-bundle for the front end — this is the documented, glue-free path.
  • Pin integrated/menu-bundle and its Integrated siblings together and record a tested matrix (PHP version, Symfony version, DB driver).
  • If you're on Laravel, budget for a framework adapter (service provider bridging the Symfony event dispatcher + container). The event contract maps cleanly onto Laravel's dispatcher, but that adapter is your work, not the package's.

Gotchas and Tips

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.
  • The provider chain — you can typically register a custom provider if you need behavior the DB provider doesn't give you.
  • The event subscriber registration — this is where you isolate failures and control ordering.

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.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource