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

lephare/menu-bundle

lephare/menu-bundle is a PHP package for defining, organizing, and rendering menus in your application. It supports nested menu items and configurable output, helping you build reusable navigation structures through a simple API and keep menu configuration consistent across your project.

View on GitHub
Deep Wiki
Context7

Getting Started

Do not composer require this. lephare/menu-bundle is a Symfony bundle (Composer type symfony-bundle, a Bundle subclass, DI Extension/Configuration compiler passes, a Twig renderer extension). In a Laravel app there is no bundle registration pipeline, so even a forced VCS install resolves against stale symfony/* constraints and then fatal-errors at boot. "Getting started" here means reading, not installing.

Where to look first, in order:

  1. *MenuBundle.php — confirms the Bundle subclass and shows which compiler passes and Twig extensions are registered. This tells you how much surface area is Symfony scaffolding versus real domain logic.
  2. DependencyInjection/Configuration.php + *Extension.php — the config tree and service wiring. This is your spec for what a config/menu.php and a MenuServiceProvider would need to look like if you port.
  3. The domain layer (Menu, MenuItem, tree assembly, active-detection, visibility rules) — the only part with reuse value.
  4. The Twig Extension — the rendering contract you'd re-express as a Blade component.

First use case to evaluate against: a nested, ordered, route-aware primary navigation with permission-based visibility. That's the canonical thing this package models, and it's the thing you'll be rewriting. A MenuItem with children, a route/url used for active matching, and visibility gated on attributes/permissions is roughly 150–250 lines of Eloquent + a Blade component — read the bundle to confirm your model is no more complex than that, then build native.

If you're in a legacy Symfony app already consuming it, the first step is not learning the API — it's running composer why lephare/menu-bundle, checking the pinned symfony/* and PHP version constraints, and confirming the test suite (if any) passes on your target runtime. That determines whether you're maintaining it or migrating off it.

Implementation Patterns

Treat the source as a reference implementation and map each bundle artifact to its Laravel equivalent. The day-to-day pattern is "read the domain layer, port the idea, discard the scaffolding."

Menu tree definition. The bundle's declarative tree (nested children, ordering) maps to either a config/menu.php array for static menus or an Eloquent model with parent_id + position for DB-editable menus. Keep the tree data separate from the rendering — that separation is the one architectural lesson worth keeping.

Active-item detection. The bundle auto-marks the item whose route matches the current request. In Laravel this is request()->routeIs($item->route) or url()->current() === $item->url, resolved once and passed into the renderer as a boolean per node. Don't recompute it inside the Blade loop.

Attribute/permission visibility. The bundle gates items on attributes and permissions. In Laravel that's a Gate::allows() check or a policy per item, evaluated during tree assembly — not during render. Assemble the visible tree first, then render, so caching stays simple.

Renderer as a pluggable boundary. The bundle exposes pluggable renderers + templates. Mirror this with a Blade component (<x-menu :tree="$tree" />) and a MenuRenderer contract so markup variants (top nav, sidebar, footer) are component props or separate views, not if ladders.

Registration pattern. The bundle collects items via compiler passes and service tags. The Laravel equivalent is a MenuRegistry singleton bound in a MenuServiceProvider, populated either explicitly (Menu::register('main', fn ($menu) => ...)) or by scanning tagged classes/attributes. Explicit registration is easier to debug and to test than tag scanning — prefer it.

Typical day-to-day workflow:

  1. Define/seed menus (config array or Eloquent rows).
  2. Assemble the tree once per request (or pull from cache), eager-loading children to avoid N+1.
  3. Prune by permission in memory against an unfiltered cached tree.
  4. Render via a Blade component in the layout.
  5. Test against fixtures: empty, single-level, deep nesting, multi-locale, permission-filtered, unpublished/deleted targets.

Caching pattern. Cache the unfiltered tree keyed on a menu version stamp, invalidate on write, then prune per-user in memory. Never cache per-user trees — the cardinality explodes and invalidation becomes impossible to reason about.

Gotchas and Tips

  • Framework mismatch is fatal, not cosmetic. This is a symfony-bundle. It will not boot under Laravel. If it lands in a shared dependency set by accident, you get a class-not-found / container resolution failure at startup — a total outage, not a broken menu.
  • Archived means you own it forever. No security response, no PHP-minor drift plan, no Symfony compatibility work. If a transitive dependency (symfony/translation, doctrine/*) gets a CVE, nobody upstream bumps the constraint. Price adoption as "adopt + own indefinitely."
  • Pin the PHP ceiling before estimating anything. Symfony 3.x/4.x-era bundles routinely use constructs removed in PHP 8 (each-by-reference, dynamic properties, curly-brace offsets). Enumerate the transitive dependency tree before writing a port plan — a stale pin colliding with your composer.lock is a classic unresolvable-conflict trap.
  • Cyclic parent_id references are a stack overflow waiting to happen. Whatever model you adopt, enforce a depth cap or use a closure table with cycle prevention at write time. An uncached recursive walk on a legitimate-looking data cycle will take the request down.
  • Stale cache after edits is the most common menu incident. Editors change a menu, invalidation misses, the site shows last week's nav. Design the cache key and invalidation path up front; a version stamp bumped on write is the cheapest correct approach.
  • Dangling references render 404s in primary navigation. A menu_item pointing at an unpublished or deleted page must be filtered at assembly, not discovered by a user clicking it.
  • Check the license before forking. An archived repo with no LICENSE file is legally ambiguous and can block a fork or closed-source redistribution. Resolve this before you vendor a single line.
  • No tests means unverified domain behavior. If you fork, the first commit is characterization tests against the current behavior — before any cleanup, before any "improvement." You're capturing what it does, not what it should do.
  • Extension points worth preserving in a port: the MenuRegistry (item collection), the renderer contract (markup), and the config tree (defaults + per-menu overrides). Everything else — Bundle subclass, DI extension, compiler passes, Twig extension — is Symfony glue with no Laravel analogue; delete it rather than translating it literally.
  • If a legacy Symfony consumer exists, migrate strangler-style: run the new Laravel menu service in parallel, diff rendered HTML across representative fixtures (empty, single-level, deep, multi-locale, permission-filtered, unpublished), and gate cutover on parity for one full release cycle. Don't decommission the old path until that holds.
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
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core