Skip to content

Creating Menus as Services

Edit this page

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') }}
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version