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

Sonata Menu Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require prodigious/sonata-menu-bundle:^3.0.7
  1. Confirm the dependency chain first. The bundle assumes Sonata Admin (and optionally Sonata Page). Check 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.
  2. Register the bundle in config/bundles.php:
    Prodigious\SonataMenuBundle\SonataMenuBundle::class => ['all' => true],
    
  3. Read the source before wiring anything. It is a small package. Look at, in this order:
    • 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.
  4. Update the schema. If the bundle ships concrete mapped entities:
    bin/console doctrine:schema:update --dump-sql   # inspect first
    bin/console doctrine:schema:update --force
    # or generate a migration and review it
    
  5. Verify admin registration at /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.
  6. First use case: create one menu ("header") with two items in Sonata Admin, then render it. The safest starting point is to resolve from the repository rather than guessing a Twig helper:
    // 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.

Implementation Patterns

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.

Gotchas and Tips

  • Maintenance status, not abandonment. 3.0.7 shows the package is still receiving care, so the old "assume no upstream fixes" posture is too pessimistic — but the bus factor is still one and the release cadence is sporadic. Pin ^3.0.7, keep the diff of your own overrides small, and only fork if you actually need an unreleased fix.
  • The yaml fix has a scope. "Compatibility with recent symfony/yaml changes" almost certainly means the bundle's own .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.
  • If the container fails to compile after upgrading, suspect config parsing before you suspect the bundle's PHP. Symptoms: 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 mapping ambiguity. Many bundles of this shape ship entities meant to be owned by your app. If 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.
  • Admin dashboard silent failures. A 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
    
  • Orphaned items. If a linked Sonata Page is deleted, a menu item may hold a dangling reference and throw on render. Guard with ?-> and null checks, and consider an onDelete="SET NULL" association or a listener that removes orphaned items.
  • N+1 on tree rendering. Self-referencing 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.
  • Hardcoded labels/translations. Admin labels and form choices are often not extracted to a translation domain. Override them in your own admin subclass in configureFormFields(), and set setTranslationDomain() on the service.
  • Don't store absolute URLs. Prefer route + parameters or a page id so router context (host, base path, locale) is applied at render time.
  • No built-in cache invalidation. If pages show stale menus after an admin edit, the cause is almost always your own cache pool, not the bundle.
  • Find the public API cheaply: 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.
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