prodigious/sonata-menu-bundle
A Symfony bundle for creating and managing hierarchical menus with Sonata Admin integration, editable menu items, and Twig rendering helpers. Configure menu providers, control item visibility, and organize site navigation cleanly in Symfony applications.
composer require prodigious/sonata-menu-bundle:^3.0.7
composer.json in the vendor directory for its constraints before installing. 3.0.7 contains a symfony/yaml compatibility fix, which is a signal that dependency ceilings have been relaxed — but a yaml fix is not an endorsement of Sonata Admin 5.x or Symfony 7. Run composer why-not prodigious/sonata-menu-bundle if resolution fails, and read the lock diff to see exactly which symfony/* and sonata-* versions 3.0.7 pulled in.config/bundles.php:
Prodigious\SonataMenuBundle\SonataMenuBundle::class => ['all' => true],
src/Entity/ — the Menu / MenuItem models and whether they are concrete entities or mapped superclasses you are expected to extend.src/Admin/ — the admin classes and their service definitions, which tell you the exact code, form fields and item types supported.src/Resources/config/ — service wiring, Doctrine mappings, and any auto-registered sonata.admin services. This is the directory 3.0.7 touched conceptually (yaml-driven config), so it is the first place to look if the bundle suddenly fails to boot.src/Twig/ or src/Block/ — whether a render helper/block service is provided.bin/console doctrine:schema:update --dump-sql # inspect first
bin/console doctrine:schema:update --force
# or generate a migration and review it
/admin. If the Menu/MenuItem admins don't appear on the dashboard, the bundle probably expects you to register them yourself (see below) — most Sonata bundles ship an admin.xml you must import or replicate.// controller
$menu = $menuRepository->findOneByCode('header');
return $this->render('home.html.twig', ['menu' => $menu]);
{% macro items(items) %}
<ul>
{% for item in items %}
<li>
<a href="{{ item.url }}">{{ item.title }}</a>
{% if item.children is not empty %}{{ _self.items(item.children) }}{% endif %}
</li>
{% endfor %}
</ul>
{% endmacro %}
Upgrading an existing install to 3.0.7: it is a patch release, so there is no migration path to write — but do run composer update symfony/yaml prodigious/sonata-menu-bundle, then bin/console cache:clear, then load the menu admin screen and one front-end page. The changed code path (yaml parsing of the bundle's own service/doctrine config) only manifests at container compile time, so a broken upgrade shows up as a boot failure or an empty admin dashboard, not as a runtime exception later.
Manual admin service registration. Sonata uses tagged services; if the bundle doesn't auto-register, declare them explicitly so you control labels and groups:
services:
app.admin.menu:
class: Prodigious\SonataMenuBundle\Admin\MenuAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: content, label: Menus }
arguments: [~, Prodigious\SonataMenuBundle\Entity\Menu, ~]
calls:
- [setTranslationDomain, ['messages']]
Item-type discrimination. Menu items typically support three targets — a raw URL, a Symfony route, or a Sonata Page reference. Render with a small resolver instead of sprinkling if in Twig:
public function resolveUrl(MenuItem $item): ?string
{
if ($item->getPage()) {
return $this->router->generate($item->getPage()->getRouteName());
}
if ($item->getRoute()) {
return $this->router->generate($item->getRoute(), $item->getParameters() ?? []);
}
return $item->getUrl();
}
Extend rather than patch. If you need a template, css_class, or locale field, subclass the entity (extends BaseMenuItem) plus the admin (extends MenuItemAdmin), override configureFormFields() with parent::configureFormFields() and add your field, then point the service at your class. This is why checking for a mapped superclass in step 3 matters. It is also what keeps 3.0.7-style patch upgrades cheap — if you vendored or forked the bundle instead (see Gotchas), rebase your fork onto 3.0.7 rather than cherry-picking the yaml fix.
Twig global for convenience. Register a lazy menu provider and expose menu('header'), so templates stay clean and you get one cacheable entry point:
$twig->addFunction(new TwigFunction('menu', fn (string $code) => $this->menus->get($code)));
Cache deliberately. Menu trees change rarely and are read on every page. Wrap resolution in a cache pool keyed on code + locale, and evict from a Doctrine postPersist/postUpdate/postRemove listener on MenuItem (or a Sonata admin hook). Sonata will not invalidate your application cache for you.
Multi-locale. Either add a locale column and filter in the repository, or create one menu per locale code (header_en, header_fr) and resolve by $request->getLocale(). The former scales better with Sonata Page's own locale handling.
Sonata Page integration. Store the page reference, not the generated URL. URLs change between environments and when slugs are edited; storing absolute URLs means stale links after any page rename.
^3.0.7, keep the diff of your own overrides small, and only fork if you actually need an unreleased fix..yml service/doctrine config no longer trips over newer Symfony YAML behaviour (flag handling, parsing of certain scalar forms, deprecation warnings promoted to errors). It does not mean the bundle now supports Symfony 7 / Sonata Admin 5. Verify in a throwaway branch, and watch the symfony/yaml constraint in your lock file — if something else in your app pins an older yaml component, 3.0.7 may not install at all.InvalidArgumentException about a service file, a sonata.admin service that vanishes, or debug:container showing nothing for Prodigious. Clear the container cache (bin/console cache:clear, and delete var/cache/* on a bad build) before deep debugging — Symfony caches compiled yaml aggressively.doctrine:schema:update produces nothing, check whether the mapping lives in Resources/config/doctrine/ or whether you must register doctrine.orm.mappings yourself. Also check for MappedSuperclass annotations — if present, you must create app-side subclasses.sonata.admin service missing its tag or with a duplicate code simply won't render. Debug with:
bin/console debug:container --tag=sonata.admin
bin/console cache:clear
?-> and null checks, and consider an onDelete="SET NULL" association or a listener that removes orphaned items.parent/children relations load lazily. Fetch a menu with ->leftJoin('m.items', 'i')->addSelect('i') or a dedicated repository method, or a 40-item menu will fire 40 queries per page.configureFormFields(), and set setTranslationDomain() on the service.router context (host, base path, locale) is applied at render time.bin/console debug:twig lists any provided functions/filters, and bin/console debug:container Prodigious shows every service the bundle registers — that's usually faster than reading the README, and faster than diffing 3.0.6 against 3.0.7 if you just want to know what changed.How can I help you explore Laravel packages today?