## Getting Started
### Minimal Setup
1. **Install the Bundle**
Add to your `composer.json`:
```bash
composer require chamber-orchestra/menu-bundle
Enable in config/bundles.php:
ChamberOrchestra\MenuBundle\ChamberOrchestraMenuBundle::class => ['all' => true],
Configure the Bundle Publish the default config:
php bin/console chamber-orchestra:menu:config
Edit config/packages/chamber_orchestra_menu.yaml to define your menu structure (e.g., main_menu, footer_menu).
First Use Case: Render a Menu in Twig
Inject the MenuBuilder service and render in a Twig template:
{{ render_menu('main_menu') }}
Or directly in PHP:
$menu = $menuBuilder->build('main_menu');
$menuRenderer->render($menu);
Define a Menu
Create a YAML config (e.g., config/menus/main_menu.yaml):
main_menu:
items:
- label: 'Home'
route: 'homepage'
active: true
- label: 'Products'
route: 'products.index'
children:
- label: 'Laptops'
route: 'products.laptops'
Dynamically build menus in code (e.g., for dynamic content):
$menu = $menuBuilder->create('dynamic_menu')
->addItem('Dashboard', 'dashboard')
->addItem('Settings')
->addChild('Profile', 'profile.edit')
->addChild('Billing', 'billing.index')
->end()
->get();
Automatically highlight the current route:
items:
- label: 'Blog'
route: 'blog.index'
active: true # Optional; auto-detected if `active` is omitted
Override logic via active_matcher in config:
chamber_orchestra_menu:
matchers:
active:
class: App\Matcher\CustomActiveMatcher
Restrict menu items by user roles:
items:
- label: 'Admin'
route: 'admin.dashboard'
roles: ['ROLE_ADMIN']
Or dynamically in code:
$menu->addItem('Admin', 'admin.dashboard')
->setRoles(['ROLE_ADMIN']);
Add badges (e.g., notifications) to menu items:
items:
- label: 'Messages'
route: 'messages.index'
badge: { text: '3', type: 'danger' }
Or programmatically:
$menu->addItem('Messages', 'messages.index')
->setBadge('3', 'danger');
Cache menus for performance:
chamber_orchestra_menu:
cache:
enabled: true
pool: 'app.cache.menu'
Clear cache via:
php bin/console cache:clear chamber_orchestra_menu
Use Twig helpers for rendering:
{% render_menu 'main_menu', {
'active_class': 'active',
'badge_class': 'badge',
'depth_limit': 2
} %}
Customize templates by overriding:
templates/chamber_orchestra_menu/menu.html.twig.
Listen to menu-building events (e.g., MenuBuildEvent):
// src/EventListener/AddDynamicItemsListener.php
public function onMenuBuild(MenuBuildEvent $event) {
$event->getMenu()->addItem('Dynamic Item', '#');
}
Register in services.yaml:
services:
App\EventListener\AddDynamicItemsListener:
tags:
- { name: 'kernel.event_listener', event: 'menu.build', method: 'onMenuBuild' }
Circular References in Menus
Avoid recursive menu structures (e.g., a child pointing to its parent). The bundle throws a CircularReferenceException if detected.
Route Matching Edge Cases
route_parameters to match dynamic segments:
route: 'product.show'
route_parameters: { slug: 'laptop' }
http:// or https:// to bypass route matching:
route: 'https://example.com'
Cache Invalidation Clear the cache after dynamic menu changes:
php bin/console cache:pool:clear app.cache.menu
Or programmatically:
$this->container->get('chamber_orchestra_menu.cache')->clear();
Twig Template Overrides Ensure your custom template extends the base template:
{% extends '@ChamberOrchestraMenu/menu.html.twig' %}
Override variables like menu_item_classes to modify rendering:
{% set menu_item_classes = menu_item_classes ~ ' custom-class' %}
Performance with Large Menus
Use depth_limit in Twig to render only top-level items:
{{ render_menu('main_menu', { 'depth_limit': 1 }) }}
Or in PHP:
$menuRenderer->setDepthLimit(1);
Dump Menu Structure Use the debug command to inspect menus:
php bin/console debug:menu
Or dump in code:
dump($menuBuilder->build('main_menu')->toArray());
Enable Verbose Logging
Add to config/packages/dev/chamber_orchestra_menu.yaml:
chamber_orchestra_menu:
debug: true
Check Route Matching Verify routes with:
php bin/console debug:router
Ensure your menu routes exist and are correctly named.
Custom Matchers
Implement ChamberOrchestra\MenuBundle\Matcher\MatcherInterface for custom logic (e.g., URL matching):
class UrlMatcher implements MatcherInterface {
public function isActive(MenuItem $item, Request $request): bool {
return str_contains($request->getUri(), $item->getRoute());
}
}
Register in config:
chamber_orchestra_menu:
matchers:
url:
class: App\Matcher\UrlMatcher
Custom Item Types
Extend MenuItem to add metadata:
class ExtendedMenuItem extends MenuItem {
private ?string $customData;
public function setCustomData(string $data): self {
$this->customData = $data;
return $this;
}
}
Use in Twig:
{{ menu_item.customData }}
Dynamic Item Providers Fetch menu items from a database or API:
class DatabaseMenuProvider implements MenuProviderInterface {
public function getItems(string $menuName): array {
return $entityManager->getRepository(MenuItem::class)
->findBy(['menu' => $menuName]);
}
}
Register in services.yaml:
services:
App\Provider\DatabaseMenuProvider:
tags:
- { name: 'chamber_orchestra_menu.provider', menu: 'dynamic_menu' }
Override Default Config
Use config/packages/override/chamber_orchestra_menu.yaml to override defaults without modifying the bundle’s config.
YAML vs. PHP Config Prefer YAML for static menus; use PHP for dynamic logic:
// src/Menu/DynamicMenuBuilder.php
public function build(string $name): Menu {
$menu = new Menu($name);
// Add items dynamically...
return $menu;
}
Register as a service:
services:
App\Menu\DynamicMenuBuilder:
tags:
- { name: 'chamber_orchestra_menu.builder', menu: 'dynamic_menu' }
Priority of Config Sources Order of precedence:
config/packages/override/chamber_orchestra_menu.yamlconfig/packages/chamber_orchestra_menu.yamlHow can I help you explore Laravel packages today?