KnpMenuBundle Documentation
repository·master·Indexed 23 days ago
https://github.com/knplabs/knpmenubundleA Symfony integration for the KnpMenu PHP library that allows developers to build and manage complex menus. It includes features for creating custom MenuProviders, registering custom renderers, extending menus via Symfony Events, and integrating with Twig and I18n translation mechanisms.
What's inside KnpMenuBundle
- KnpMenuBundle is a Symfony bundle that integrates the KnpMenu PHP library into Symfony applications. It provides a feature-rich and easy-to-implement way to manage menus within a Symfony environment.
Extend menus using Symfony Events
masterTo allow different parts of your application to hook into and modify a menu during its construction, you can implement a pattern using the Symfony
EventDispatchercomponent. This involves three main steps: creating a menu builder that dispatches a custom event, defining a custom Event object to carry the menu and factory, and creating listeners that react to that event to add items.// The builder dispatches the event $this->eventDispatcher->dispatch( new ConfigureMenuEvent($this->factory, $menu), ConfigureMenuEvent::CONFIGURE );How to implement a Menu Builder service
masterA Menu Builder is a class that uses
Knp\Menu\FactoryInterfaceto construct menu items. You can include multiple methods within a single builder class to handle different menus (e.g., a main menu and a sidebar menu).// src/Menu/MenuBuilder.php namespace App\Menu; use Knp\Menu\FactoryInterface; use Symfony\Component\HttpFoundation\RequestStack; class MenuBuilder { private $factory; public function __construct(FactoryInterface $factory) { $this->factory = $factory; } public function createMainMenu(RequestStack $requestStack) { $menu = $this->factory->createItem('root'); $menu->addChild('Home', ['route' => 'homepage']); // ... add more children return $menu; } }Creating Menus as Services
masterYou can register menus as Symfony services to make them available for rendering in templates. However, registering a menu directly as a service has limitations: it does not allow the use of builder options and reuses the same instance, which can cause side-effects if the menu is rendered multiple times.
Recommended Approach: Instead of registering the menu object itself as a service, register a Menu Builder class as a service and use it as a factory to create your menu services.
Render menus in Twig or PHP
masterOnce a menu is defined, you can render it directly in your templates or controllers.
Twig
Use the
knp_menu_renderfunction. You can pass an options array as the second argument to control rendering behavior (e.g.,depth,currentAsLink).{# Basic rendering #} {{ knp_menu_render('my_main_menu') }} {# Rendering with options #} {{ knp_menu_render('my_main_menu', {'depth': 2, 'currentAsLink': false}) }}PHP
Use the
rendermethod on the menu service (typically available in the view array in controllers).<?php echo $view['knp_menu']->render('my_main_menu') ?> <?php echo $view['knp_menu']->render('my_main_menu', [ 'depth' => 2, 'currentAsLink' => false, ]) ?>{{ knp_menu_render('my_main_menu') }}Create and register a Menu Event Listener
masterA listener can modify the menu by retrieving it from the event object and calling
addChild().To register a listener in Symfony, add it to your
services.yamlwith thekernel.event_listenertag. Alternatively, you can implementEventSubscriberInterfaceand use thekernel.event_subscribertag.// src/Acme/AdminBundle/EventListener/ConfigureMenuListener.php namespace Acme\AdminBundle\EventListener; use App\Event\ConfigureMenuEvent; class ConfigureMenuListener { public function __invoke(ConfigureMenuEvent $event) { $menu = $event->getMenu(); $menu->addChild('Matches', ['route' => 'versus_rankedmatch_acp_matches_index']); $menu->addChild('Participants', ['route' => 'versus_rankedmatch_acp_participants_index']); } }# config/services.yaml services: app.admin_configure_menu_listener: class: Acme\AdminBundle\EventListener\ConfigureMenuListener tags: [kernel.event_listener]Render Menus in Twig Templates
masterOnce a menu is registered, you can render it in Twig using two different approaches depending on whether you need to pass custom options.
Simple Rendering
If you just want to render a menu by its name/alias without passing extra options:
{{ knp_menu_render('main') }}Rendering with Custom Options
If your menu builder method uses the
$optionsarray to conditionally add items, you must first retrieve the menu object usingknp_menu_getbefore rendering:{% set menu = knp_menu_get('sidebar', [], {include_homepage: false}) %} {{ knp_menu_render(menu) }}{# Simple render #} {{ knp_menu_render('main') }} {# Render with options #} {% set menu = knp_menu_get('sidebar', [], {include_homepage: false}) %} {{ knp_menu_render(menu) }}Create menus using the Naming Convention
masterKnpMenuBundle allows you to build menus by defining a
Builderclass within one of your bundles. This method uses a specific naming convention to locate the class and the specific menu method to execute.Implementation Steps
- Create a Builder Class: Place a class in the
Menudirectory of one of your bundles (e.g.,src/AppBundle/Menu/Builder.php). - Define Menu Methods: Each method in the class represents a specific menu. These methods must accept two arguments:
Knp\Menu\FactoryInterface $factory: Used to create the menu items.array $options: An array of options passed to the builder.
- Return the Menu: The method must return an object implementing
Knp\Menu\ItemInterface.
Naming Convention
To reference a menu, use a three-part string format:
bundle:class:methodExample: If your bundle is
AppBundle, your class isBuilder, and your method ismainMenu, the identifier isApp:Builder:mainMenu.Limitations
This convention relies on Symfony's bundle structure. Projects that keep code outside of a bundle (such as standard Symfony Flex skeletons) cannot use this specific naming convention method.
// src/AppBundle/Menu/Builder.php namespace AppBundle\ Menu; use Knp\Menu\FactoryInterface; use Knp\Menu\ItemInterface; final class Builder { public function mainMenu(FactoryInterface $factory, array $options): ItemInterface { $menu = $factory->createItem('root'); $menu->addChild('Home', ['route' => 'homepage']); $menu->addChild('Latest Blog Post', [ 'route' => 'blog_show', 'routeParameters' => ['id' => 1] ]); $menu->addChild('About Me', ['route' => 'about']); $menu['About Me']->addChild('Edit profile', ['route' => 'edit_profile']); return $menu; } }- Create a Builder Class: Place a class in the
Access KnpMenuBundle documentation
masterFor detailed usage instructions, configuration, and API references, refer to the official documentation hosted on the Symfony website or the local
docsdirectory within the bundle repository.Official Documentation: https://symfony.com/bundles/KnpMenuBundle/current/index.html
Register Menu Builders in Symfony Services
masterIf your application uses Symfony autoconfiguration, simply adding the
#[AsMenuBuilder(name: '...')]attribute to your method is sufficient to register the menu.If you are not using autoconfiguration, you must manually register the service in
config/services.yamlusing theknp_menu.menu_buildertag. You must specify themethodthat builds the menu and analias(which acts as the identifier for retrieval).# config/services.yaml services: app.menu_builder: class: App\Menu\MenuBuilder arguments: ["@knp_menu.factory"] tags: - { name: knp_menu.menu_builder, method: createMainMenu, alias: main }Translate menu labels using I18n
masterKnpMenuBundle translates all menu item labels by default using the standard Symfony translation mechanism. When you create a menu item with a label, the bundle looks for a translation of that label in your application's message domain.
For example, if you create a menu item with the label
'Home', you can provide translations in your project's translation files (YAML, XLIFF, or PHP) under the defaultmessagesdomain.$menu = $factory->createItem('root'); $menu->addChild('Home', ['route' => 'homepage']); $menu->addChild('Login', ['route' => 'login']);Register a custom MenuProvider in Symfony services
masterTo make your custom provider available to the KnpMenuBundle, you must register it as a service and tag it with
knp_menu.provider. Ensure you pass the@knp_menu.factoryservice as an argument to your provider's constructor.# config/services.yaml or app/config/services.yml services: app.menu_provider: class: App\Provider\CustomMenuProvider arguments: - '@knp_menu.factory' tags: - { name: knp_menu.provider }