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

id4v/menu-bundle

ID4V MenuBundle is a PHP package for defining, organizing, and rendering configurable navigation menus. Build menu structures with items and nested links, then output them consistently across your application. Its simple API helps keep menu configuration maintainable and reusable.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is not a Laravel package. id4v/menu-bundle is a Symfony bundle built for the Symfony 2.x/3.x era and depends on sonata-project/admin-bundle and Doctrine ORM. You cannot composer require it into a Laravel app — Composer will fail on symfony/symfony, symfony/framework-bundle, and Sonata constraints long before autoload. Treat this assessment as "what can I learn from / port out of it," not "what can I install."

Minimal steps if you find yourself in a legacy Symfony app that already has it:

  1. composer require id4v/menu-bundle (expect to pin symfony/* to ~2.8|~3.0 and Sonata Admin to 2.x/3.x).
  2. Register the bundle in app/AppKernel.php (new Id4v\MenuBundle\Id4vMenuBundle()), and import its admin routing in app/config/routing.yml so the Sonata screens appear.
  3. Run app/console doctrine:schema:update --dump-sql and inspect before executing — the bundle ships entity mappings that will create menu/menu-item tables.
  4. Open src/ first. Look at the Entity/ directory (the menu and menu-item models and their parent/position relationship), then Admin/ (the Sonata admin classes), then Resources/views/ (the render templates). Those three directories are the package; there is very little else.
  5. First real use case: an editor-managed navigation menu — header/footer links that a non-developer maintains in Sonata instead of in a Twig template.

For a Laravel codebase, the fastest path to value is to copy the data model, not the code: a menus table (id, name, slug) and a menu_items table (id, menu_id, parent_id, label, route/url, target, position, enabled). That is ~40 lines of migration and gets you 90% of what this bundle does.

Implementation Patterns

Model shape. The bundle's core idea is a Menu aggregate containing self-referencing MenuItem rows ordered by an integer position, with parent_id expressing nesting. It is adjacency-list, not nested-set — cheap to write, recursive to read. Mirror that exactly if you port it; don't "improve" it to nested set unless you actually need deep trees.

Route-name vs raw URL. The useful pattern here is storing either a Symfony route name plus serialized parameters or a literal URL, and resolving the route at render time. Copy this into Laravel as route_name + route_params (JSON) columns with a url fallback, and resolve with route($name, $params) in the accessor. It keeps menus portable across environments where absolute URLs differ.

Resolve-then-render, once per request. Fetch the menu by slug (header, footer), hydrate the full item set in a single query, build the tree in PHP, and hand a nested array to the view. Do not lazy-load children per node — that's the classic N+1 this kind of bundle invites.

// Laravel port of the pattern
$items = MenuItem::where('menu_id', $menu->id)
    ->where('enabled', true)
    ->orderBy('position')
    ->get();

$tree = $items->groupBy('parent_id'); // then recurse on $tree[null], $tree[$id]

Recursive partial. One Blade partial that includes itself:

<ul>
@foreach ($nodes as $node)
    <li>
        <a href="{{ $node->resolvedUrl() }}" target="{{ $node->target }}">{{ $node->label }}</a>
        @if (!empty($node->children))
            @include('partials.menu-items', ['nodes' => $node->children])
        @endif
    </li>
@endforeach
</ul>

Admin integration. In Sonata the pattern is: one Admin class per entity, a form field of type sonata_type_model_list for the parent, and position as a numeric input. The transferable lesson for Laravel is the same: expose menus through a resource controller (Filament/Nova/Backpack) with a parent select scoped to the same menu — never allow a parent from a different menu, or you get orphaned cross-links.

Cache the rendered HTML, invalidate on save. Menus change rarely and render on every page. Cache the built tree or the rendered string keyed by menu slug, and bust it in the model's saved/deleted events. This bundle leaves that to you; do it explicitly rather than relying on query caching.

Per-location menus. One menu per slot (header, footer, sidebar) looked up by slug is the cleanest contract and the one this bundle nudges you toward. Avoid a "one giant menu with visibility flags" design.

Gotchas and Tips

  • It is abandoned. Last release 2015-09-29, one star, zero dependents. Assume no security patches, no PHP 8 compatibility, and no Sonata Admin 4 support (the Admin base class signatures changed). Do not put this in a new build.
  • Version-resolution hell. If you must install it, expect to pin Symfony and Sonata simultaneously; the constraints predate modern Flex recipes, so there's no config/bundles.php auto-registration.
  • Adjacency lists and ordering. Position ordering has no tiebreaker — duplicate positions produce nondeterministic ordering across databases. Add position, id to the ORDER BY when you port it.
  • Orphan and cycle risk. Nothing in the model prevents a menu item being its own ancestor, or a parent being deleted while children remain. Add a cascadeOnDelete on the parent foreign key and validate "parent is in the same menu and is not a descendant" in the form request.
  • Route parameters are strings after a round-trip. If you store route_params as JSON, integers come back as ints but booleans/objects won't survive. Keep params to scalars.
  • Sonata admin is its own ecosystem. Debugging starts at bin/console sonata:admin:list and bin/console debug:container | grep id4v; routing problems are almost always a missing routing.yml import or an ACL role lacking EDIT/LIST on the admin service.
  • Template overrides are the main extension point. Symfony convention: drop a file in app/Resources/Id4vMenuBundle/views/ to shadow the bundle's template. In a port, that means keeping the recursive partial swappable per project rather than hard-coding markup in a component.
  • Service decoration beats forking. The bundle is tiny enough that its manager service can be decorated to add caching, URL resolution, or locale filtering without touching vendor code. If you're maintaining a legacy app on this, decorate; don't fork.
  • What to use instead. For Laravel: spatie/laravel-menu or lavary/laravel-menu give you the builder/rendering side; for admin management, a plain menus + menu_items table plus your existing admin panel is less work than wiring an unfamiliar Symfony bundle. For Symfony today: knplabs/knp-menu-bundle (rendering) plus Sonata's own sonata-page-bundle, or just a custom entity.
  • The one genuinely reusable idea is the "menu as data, managed in admin, resolved by route name, cached per slug" workflow. Steal that pattern; leave the 2015 code behind.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons