darvinstudio/darvin-menu-bundle
Install the Bundle Add the package via Composer:
composer require darvinstudio/darvin-menu-bundle
Enable the bundle in config/bundles.php:
return [
// ...
DarvinStudio\MenuBundle\DarvinMenuBundle::class => ['all' => true],
];
Configure a Menu
Define a menu in config/packages/darvin_menu.yaml:
darvin_menu:
menus:
header: ~ # Minimal config; items are defined via annotations or YAML later
First Render Use the ESI controller to render the menu in Twig:
{{ render_esi(controller('darvin_menu.controller.menu', {
'buildOptions': {'menu': 'header', 'depth': 2},
'renderOptions': {'template': 'menu/header.html.twig'}
})) }}
menu/header.html.twig exists in your templates directory.depth parameter limits nesting (e.g., 2 shows top-level + one sub-level).Define Menu Items
Use annotations (e.g., @MenuItem) on controllers/actions or configure via YAML in config/packages/darvin_menu.yaml:
darvin_menu:
menus:
header:
items:
- route: 'homepage'
label: 'Home'
- route: 'about'
label: 'About'
children:
- route: 'team'
label: 'Team'
Dynamic Menu Building
darvin_menu:
menus:
admin:
items:
- route: 'admin_dashboard'
label: 'Dashboard'
acl: 'ROLE_ADMIN'
@MenuItem for automatic inclusion:
use DarvinStudio\MenuBundle\Annotation\MenuItem;
class AdminController {
/**
* @MenuItem(menu="admin", label="Users", route="admin_users")
*/
public function usersAction() { ... }
}
Conditional Rendering
acl or condition in YAML to show/hide items:
items:
- route: 'user_profile'
label: 'Profile'
condition: 'app.user.is_authenticated'
buildOptions:
{{ render_esi(controller('darvin_menu.controller.menu', {
'buildOptions': {
'menu': 'header',
'context': {'show_promo': true}
}
})) }}
Template Customization
menu/default.html.twig) by specifying a custom path in renderOptions:
{{ render_esi(controller('darvin_menu.controller.menu', {
'renderOptions': {'template': 'AppBundle:menu/custom.html.twig'}
})) }}
- Extend the base template to add classes or modifiers:
```twig
{% extends 'menu/default.html.twig' %}
{% block menu_item_class %}
{{ parent() }} active
{% endblock %}
Menu Switcher
{{ render_esi(controller('darvin_menu.controller.menu_switcher', {
'menu': 'header',
'activeMenu': 'footer'
})) }}
Caching
darvin_menu:
menus:
header:
cache: true
cache_lifetime: 3600 # 1 hour
ESI Controller Dependency
framework.esic option is enabled in config/packages/framework.yaml:
framework:
esic: true
Annotation Processing
@MenuItem) require the doctrine/annotations bundle. Add it if missing:
composer require doctrine/annotations
php bin/console cache:clear
Depth Limitation
depth parameter in buildOptions is inclusive. A value of 1 shows only top-level items, while 2 includes one level of children. Misconfiguration may lead to missing or excessive nesting.Route Resolution
route keys must resolve to valid Symfony routes. Use absolute routes (e.g., _route: 'app_home') or ensure the route is loaded before rendering.Template Inheritance
menu_item_class or menu_item_attributes. Always extend the parent template to avoid breaking functionality.Check Menu Dump Use the debug command to inspect menu structure:
php bin/console darvin:menu:dump
var/log/darvin_menu_dump.log.Log Build Errors
Enable debug mode in config/packages/dev/darvin_menu.yaml:
darvin_menu:
debug: true
var/log/dev.log.ESI Issues
darvin_menu.controller.menu).menu/header.html.twig exists).fastcgi_hide_esi or Apache mod_esi).Reuse Menus Define menus once in YAML and reuse them across templates:
{# Footer menu #}
{{ render_esi(controller('darvin_menu.controller.menu', {
'buildOptions': {'menu': 'footer', 'depth': 1}
})) }}
Localization
Use Twig’s trans filter for labels:
darvin_menu:
menus:
header:
items:
- route: 'homepage'
label: 'menu.home' # Translatable key
{{ render_esi(controller('darvin_menu.controller.menu', {
'renderOptions': {'translation_domain': 'messages'}
})) }}
Dynamic Items Add items programmatically via events:
use DarvinStudio\MenuBundle\Event\MenuBuildEvent;
public function onMenuBuild(MenuBuildEvent $event) {
$menu = $event->getMenu();
$menu->addChild('dynamic_item', [
'route' => 'dynamic_route',
'label' => 'Dynamic Label'
]);
}
Register the subscriber in services.yaml:
services:
App\EventSubscriber\MenuSubscriber:
tags:
- { name: kernel.event_subscriber }
Performance
cache_lifetime: 0 to disable caching for dynamic menus.Testing Mock the ESI controller in PHPUnit:
$this->container->get('twig')->addFunction(
new \Twig_SimpleFunction('render_esi', function ($args) {
return $this->renderView('menu/header.html.twig', $args);
})
);
How can I help you explore Laravel packages today?