KnpMenuBundle Documentation

repository·master·Indexed 23 days ago

https://github.com/knplabs/knpmenubundle

A 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.

Tokens
7.5K
Snippets
23
Records
32
Agent score
80%

What's inside KnpMenuBundle

  1. Extend menus using Symfony Events

    master

    To allow different parts of your application to hook into and modify a menu during its construction, you can implement a pattern using the Symfony EventDispatcher component. 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
    );
  2. How to implement a Menu Builder service

    master

    A Menu Builder is a class that uses Knp\Menu\FactoryInterface to 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;
        }
    }
  3. Creating Menus as Services

    master

    You 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.

  4. Render menus in Twig or PHP

    master

    Once a menu is defined, you can render it directly in your templates or controllers.

    Twig

    Use the knp_menu_render function. 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 render method 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') }}
  5. Create and register a Menu Event Listener

    master

    A 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.yaml with the kernel.event_listener tag. Alternatively, you can implement EventSubscriberInterface and use the kernel.event_subscriber tag.

    // 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]
  6. Render Menus in Twig Templates

    master

    Once 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 $options array to conditionally add items, you must first retrieve the menu object using knp_menu_get before 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) }}
  7. Create menus using the Naming Convention

    master

    KnpMenuBundle allows you to build menus by defining a Builder class 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

    1. Create a Builder Class: Place a class in the Menu directory of one of your bundles (e.g., src/AppBundle/Menu/Builder.php).
    2. 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.
    3. 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:method

    Example: If your bundle is AppBundle, your class is Builder, and your method is mainMenu, the identifier is App: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;
        }
    }
  8. Register Menu Builders in Symfony Services

    master

    If 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.yaml using the knp_menu.menu_builder tag. You must specify the method that builds the menu and an alias (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 }
  9. Translate menu labels using I18n

    master

    KnpMenuBundle 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 default messages domain.

    $menu = $factory->createItem('root');
    $menu->addChild('Home', ['route' => 'homepage']);
    $menu->addChild('Login', ['route' => 'login']);
  10. Register a custom MenuProvider in Symfony services

    master

    To 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.factory service 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 }