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

shegroup/menu-bundle

A PHP package from SheGroup for defining, organizing, and rendering navigation menus. Build menu trees, nest items, and output them in your templates, with a simple structure that drops into existing PHP applications and keeps navigation configuration maintainable.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the reality check. Despite the Composer name, shegroup/menu-bundle is a Symfony bundle, not a Laravel package. The giveaway is the naming (*Bundle), the symfony-bundle package type, and the expected DependencyInjection/*Extension.php + Resources/config/services.yaml layout. There is no Illuminate\Support\ServiceProvider, no extra.laravel block, and no facade — so composer require shegroup/menu-bundle in a Laravel app will install code that Laravel never boots. Zero stars, zero dependents, and a September 2023 release mean you are adopting unmaintained code with no community safety net.

Read these first, in order:

  1. composer.json — confirm the type, the require block (Symfony and Doctrine version constraints tell you how portable the code is), and the autoload PSR-4 prefix.
  2. src/Entity/ (or src/Model/) — the Menu / MenuItem classes are the actual value here. Everything else is Symfony plumbing.
  3. src/DependencyInjection/ and Resources/config/ — shows which services exist and which are public/aliased.
  4. src/Twig/ — the rendering extension; this is the piece you will rewrite for Blade.
  5. Resources/views/ — the reference markup for nested menus.

Minimal install (Symfony host):

composer require shegroup/menu-bundle
// config/bundles.php
return [
    SheGroup\MenuBundle\MenuBundle::class => ['all' => true],
];

Then run Doctrine migrations if the bundle ships mapping, and confirm the entity manager picks up the mapping via doctrine.orm.mappings in config/packages/doctrine.yaml.

First use case: a DB-backed, admin-editable nested navigation. Seed one Menu with code = 'main', attach a handful of MenuItem rows with parent and position, then render it in a layout. That single flow exercises the model, the tree builder, and the renderer — the three things you will actually depend on.

For Laravel specifically, treat the bundle as a reference implementation to port, not a dependency. Copy the entity classes into app/Models, convert Doctrine annotations/attributes to Eloquent, and write your own tree service. A half-integrated bundle is worse than 200 lines of Eloquent you own.

Implementation Patterns

Model the adjacency list explicitly. The bundle's items are parent/position ordered. The daily-use pattern is a single query plus in-memory tree assembly, never recursive lazy loading:

$items = MenuItem::where('menu_id', $menu->id)
    ->orderBy('position')
    ->get();

$tree = $items->groupBy('parent_id'); // keyed by parent, root is null/0

Then render recursively. Build a small MenuTree service that returns a nested array of DTOs so your Blade partial never touches models directly — that keeps you free to swap storage later.

Render with a recursive Blade partial, not a helper.

{{-- resources/views/menus/items.blade.php --}}
<ul class="menu">
  @foreach ($items as $item)
    <li class="{{ $item->isActive ? 'active' : '' }}">
      <a href="{{ $item->url }}">{{ __($item->label) }}</a>
      @if ($item->children->isNotEmpty())
        @include('menus.items', ['items' => $item->children])
      @endif
    </li>
  @endforeach
</ul>

Resolve active state against the current route, once, at build time. The bundle's Twig extension typically compares a stored route name to the request. In Laravel:

$current = request()->route()?->getName();

foreach ($items as $item) {
    $item->isActive = $item->route === $current;
}
// bubble up: a parent is active if any descendant is active

Prefer route names over hardcoded URLs — it survives domain and prefix changes. Fall back to Str::startsWith($current, $item->route . '.') only when you deliberately want section-level highlighting.

Gate the menu, don't hide links in the view. Attach a permission/role column to items and filter in the builder:

->filter(fn ($item) => $item->permission === null || Gate::allows($item->permission))

Do it before tree assembly, otherwise you get orphaned parents with no visible children. Prune empty parents after filtering.

Multiple menus by code. One table, one code column (main, footer, account), one cached result per code. This is the single most useful extension over a naive implementation and matches how the bundle's Menu entity is usually shaped.

Cache the built tree, keyed by menu code and locale. Tree assembly is cheap but runs on every request; cache for an hour and bust it from model events:

static::saved(fn () => Cache::forget("menu:main:".app()->getLocale()));
static::deleted(fn () => Cache::forget("menu:main:".app()->getLocale()));

Admin editing is where day-to-day leverage lives. Wire a Filament/Nova resource over the same table with a parent_id select and a drag-ordered position column; the bundle gives you the schema shape, your admin panel gives you the workflow. Avoid hand-editing rows in a DB client — ordering bugs are silent.

Integration tips:

  • Wrap everything behind one MenuRenderer::render(string $code) call so views have exactly one dependency.
  • Localize labels by storing translation keys (menu.about) rather than per-locale rows; if the bundle stores per-locale labels instead, mirror that with a menu_item_translations table.
  • Write factories and one feature test asserting tree shape and active state — it is the only regression guard you'll have for unmaintained code.

Gotchas and Tips

  • The Laravel trap. Auto-discovery will not fire. If a tutorial tells you to composer require and start using it, it assumes Symfony. You will get "class not found" at best, and a silently dead menu at worst if you register a bootleg provider that half-wires Doctrine.
  • Doctrine vs Eloquent. The entities use Doctrine mapping (attributes or annotations depending on the release), ArrayCollection, and EntityManager. There is no Illuminate support anywhere. Budget a real port, not an afternoon.
  • No N+1 safety net. $item->children on a Doctrine association is lazy by default — a naive recursive render can issue one query per node. Eager-load (fetch="EAGER" or a fetch-join) or pre-group as shown above.
  • Empty vs null. Tree builders in bundles of this vintage often return null for an empty menu rather than [], which will blow up @foreach. Normalize at your service boundary.
  • Route name vs URL. Items frequently store a raw URL. If you later rename routes, half the menu silently 404s. Migrate to route names early; keep a target/external flag for genuinely absolute links.
  • Cache invalidation is on you. The bundle has no cache layer worth relying on. Bust on item save, menu rename, and locale change; forget all codes if you change the tree in a seeder.
  • Twig extension is not portable. Don't attempt to register it, don't install Twig in a Laravel app to satisfy it. Delete it and write the Blade partial; the logic you need is a dozen lines.
  • Version and PHP constraints. Last release 2023-09-27. Check the require block against your PHP and Symfony/Doctrine versions before assuming compatibility with current framework majors — an unmaintained bundle pinned to an older Symfony major will fight your composer update.
  • Extension points, if you stay on Symfony: override Resources/views via twig.paths, decorate the menu service rather than forking, and use the bundle config tree for defaults. If you fork for Laravel, keep the MIT header and pin the commit SHA in composer.json (dev-main#<sha>) so an upstream force-push can't break you.
  • Support reality. Zero stars and zero dependents means no one will answer an issue. Vendor the code ("type": "path" or a private fork), add your own tests, and assume you are the maintainer from day one. MIT licensing makes that legally trivial — the cost is purely maintenance.
  • When not to use it: if you need a menu with more than ~50 items per menu, nested-set or materialized-path storage will outperform the adjacency list this bundle implies. If you need runtime, code-defined menus only, spatie/laravel-menu or a plain config array is a better fit and actively maintained.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package