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

zicht/menu-bundle

ZichtMenuBundle provides a flexible, configurable menu system for Symfony applications. Define hierarchical menus, attach routes or URLs to items, control visibility with security checks, and render navigation in Twig using customizable templates and item types.

View on GitHub
Deep Wiki
Context7

Getting Started

Coming from Laravel, the mental model shifts: this is a Symfony bundle, not a Composer package you php artisan into. There is no service provider auto-discovery — Symfony's bundles.php + Flex recipe wiring does that job.

Minimal steps:

  1. composer require zicht/menu-bundle (verify the constraints against your Symfony version — this is a small, low-traffic package, so pin compatible versions explicitly).
  2. Register Zicht\MenuBundle\ZichtMenuBundle in config/bundles.php. In Symfony 4+/Flex this may be automatic; check config/bundles.php before assuming.
  3. The bundle ships Doctrine entities (typically Menu and MenuItem). Run bin/console doctrine:schema:update --dump-sql first — do not --force until you've read the diff. Alternatively generate a migration: bin/console doctrine:migrations:diff.
  4. Confirm it wires into knplabs/knp-menu-bundle (the conventional dependency for menu bundles). If KnpMenuBundle isn't installed, install it — this package almost certainly builds on top of it rather than replacing it.
  5. Look first at src/Zicht/MenuBundle/ — specifically:
    • Entity/ for Menu / MenuItem (your data contract),
    • Menu/ or Builder/ for the runtime builder,
    • DependencyInjection/Configuration.php for configurable options.
  6. First use case that gives you signal fastest: create a Menu row, add a MenuItem pointing to an existing route, then render it in a template with knp_menu_render('your_menu_alias') (or the bundle's equivalent alias).

If you can't find a README example that matches your Symfony version, stop and read the Resources/config/services.xml — Symfony bundles from this era often wire services with XML, and that file is the real source of truth for service IDs and aliases you'll inject.

Implementation Patterns

Menu as persisted data, not code. Unlike Laravel's spatie/laravel-menu (where you build menus in PHP closures), this bundle's value is that menus live in the database as Menu / MenuItem rows. Treat it as a content type, not config. Your admin CRUD (Sonata, EasyAdmin, or custom) is where editors compose the menu; your templates just render it.

Repository → builder → KnpMenu. The typical flow:

  • Inject the bundle's menu factory/repository.
  • Fetch by alias ($menuFactory->getMenu('primary-nav') or similar).
  • Feed it to KnpMenuRenderer.

Register your own alias-to-menu mapping in services.yaml so templates stay dumb:

# config/services.yaml
App\Menu\PrimaryNavFactory:
    arguments:
        $menuRepository: '@zicht_menu.menu_repository'
    tags: ['knp_menu.factory']

Multi-locale menus. If you have multiple menus per locale, either create one Menu per locale or hang locale onto MenuItem's URL resolution. The safe pattern is one menu row per (alias, locale) pair, keyed by alias — then your resolver picks by $request->getLocale().

Route vs. URL items. Persist MenuItem entries that either reference a Symfony route name plus parameters or a raw URL. Prefer route names so router:match still works and so links survive domain changes. Resolve at render time, not at save time.

Doctrine relations & cascade. Menu ↔ MenuItem is parent/child with a self-referencing relation on MenuItem for nesting. Configure cascade: ['persist', 'remove'] on the Menu→items side and orphanRemoval: true — otherwise deleting a menu leaves orphaned items. This is the single most common production bug with tree entities.

Integration with KnpMenu's MenuBuilder. Extend the bundle's factory to attach route/uri via $builder->addChild(...)->setUri(...). If the bundle exposes a MenuItemInterface, implement it on your own entity subclass so custom fields (e.g. target, rel, icon) survive into the KnpMenu item object.

Testing. Use KernelTestCase with a real kernel and a test DB. Assert against the built Menu object ($menu->getChild('home')) rather than rendered HTML — HTML assertions against this bundle break between KnpMenu minor versions.

Gotchas and Tips

  • Symfony version drift. Score 0 / 0 dependents + a low stars count is a real signal: this bundle was written for a specific Symfony era. Check composer.json's symfony/* constraints first. If it requires Symfony 2.x/3.x and you're on 6/7, expect to swap MenuBuilder internals, ContainerAwareCommand, and any ServiceController references.
  • No Laravel-style Facades. Services are injected by ID. Grep Resources/config/services.xml (or services.php) for the exact service IDs — they're not always what the docs suggest, and the zicht_menu.* prefix is not guaranteed.
  • Doctrine schema ownership. The bundle ships entities but does not own migrations. If you later want to add columns to MenuItem (very likely), you need to either subclass the entity (mapping inheritance — tricky) or fork/vendor the bundle. Decide early; retrofitting is painful.
  • Tree rebuild after save. Nested-set / parent-pointer trees get stale if you bulk-insert items. If you see missing children in the render output, rebuild the tree or re-persist via the entity manager rather than raw SQL.
  • Caching. Symfony's router and KnpMenu caching will happily serve stale menus after an editor changes a MenuItem. Hook Menu / MenuItem lifecycle events (postPersist, postUpdate, postRemove) to clear the relevant cache pool, or the menu will "not update" for ten minutes and eat an afternoon.
  • Debugging render issues. Drop to bin/console debug:container zicht_menu (or with --show-private) to confirm services are registered. If knp_menu_render throws "no menu found for alias", the alias resolution is failing — trace MenuRepository::findOneByAlias() before touching the template.
  • Extension points. Most real customization goes through: (a) overriding templates in templates/bundles/ZichtMenuBundle/, (b) implementing the bundle's MenuItem interface on a subclass, or (c) replacing the factory service via a compiler pass. There's rarely a clean "configure and done" path for custom item attributes.
  • Migration away. Because the persistence model is simple (two tables), migrating off this bundle to KnpMenu + your own thin entity layer is usually cheaper than upgrading it across Symfony majors. Budget a day, not a week — but do it if you hit Symfony 5+.
  • Security. MenuItem::url persisted as a raw string is an admin-only field in practice. If exposing it to non-trusted editors, sanitize on save — this bundle almost certainly does not.
  • Low community surface. No dependents and near-zero stars means you're the maintainer if it breaks. Fork it, pull it into vendor/ via a VCS repo ("repositories": [{"type": "vcs", ...}]), and pin a commit hash — don't rely on Packagist as-is for a long-lived app.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle