Creating Menus as Services
Note
Registering a menu as service comes with several limitations:
- it does not allow to use builder options
- it reuses the same instance several times in case you render the same menu several times, which can have weird side-effects.
It is recommended to register only menu builders as services instead.
Start by creating a builder for your menu. You can stick as many menus into a builder as you want, so you may only have one (or just a few) of these builder classes in your application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// 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;
}
}
Next, register two services: one for your menu builder, and one for the menu
object created by the createMainMenu
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# config/services.yaml
services:
app.menu_builder:
class: App\Menu\MenuBuilder
arguments: ["@knp_menu.factory"]
app.main_menu:
class: Knp\Menu\MenuItem # the service definition requires setting the class
factory: ["@app.menu_builder", createMainMenu]
arguments: ["@request_stack"]
tags:
- { name: knp_menu.menu, alias: main } # The alias is what is used to retrieve the menu
# ...
You can now render the menu directly in a template via the name given in the
alias
key above:
1
{{ knp_menu_render('main') }}
Suppose now we need to create a second menu for the sidebar. The process is simple! Start by adding a new method to your builder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Menu/MenuBuilder.php
// ...
class MenuBuilder
{
// ...
public function createSidebarMenu(RequestStack $requestStack)
{
$menu = $this->factory->createItem('sidebar');
$menu->addChild('Home', ['route' => 'homepage']);
// ... add more children
return $menu;
}
}
Now, create a service for just your new menu, giving it a new name, like
sidebar
:
1 2 3 4 5 6 7 8 9 10
# config/services.yaml
services:
app.sidebar_menu:
class: Knp\Menu\MenuItem
factory: ["@app.menu_builder", createSidebarMenu]
arguments: ["@request_stack"]
tags:
- { name: knp_menu.menu, alias: sidebar } # Named "sidebar" this time
# ...
It can now be rendered, just like the other menu:
1
{{ knp_menu_render('sidebar') }}