Comment créer un fil d’Ariane avec le KnpMenuBundle

Share Button

Read the English version

Dans ce premier post, je vais expliquer comment créer un menu fil d’Ariane avec l’excellent KnpMenuBundle. La documentation explique les bases ainsi que la méthode pour créer un menu classique, mais un fil d’Ariane a un comportement bien spécifique.

Je ne prétends pas que cette implémentation est parfaite, je veux juste expliquer comment j’ai procédé afin d’aider quiconque voudrait créer un fil d’Ariane à le faire plus vite que moi.

Installez le KnpMenuBundle

Premièrement, téléchargez et installez le bundle. Ajoutez simplement le code suivante dans votre fichier composer.json :

{
  // ...
  "knplabs/knp-menu-bundle":"dev-master",
}

Puis exécutez la commande

php composer.phar update

N’oubliez pas d’enregistrer votre bundle dans le fichier app/appKernel.php et vérifiez que
le namespace a bien été ajouté par Composer dans le fichier vendor/composer/autoload_namespaces.php.

Créez le menu

Maintenant, vous pouvez créer la classe du menu. Comme expliqué dans la documentation, il y a deux méthodes pour créer un menu : la “manière simple” (le menu sera un object que vous instancierez au besoin) et la “manière flexible” (créer le menu en tant que service). J’ai choisi la première solution pour des raisons spécifiques (je devais passer des options particulières à mon menu), mais dans la plupart des cas, il est préférable de créer le menu en tant que service. C’est ce que je vais expliquer.

Vous devez tout d’abord créer la classe du constructeur de menu (MenuBuilder.php):

<?php

  namespace Acme\MainBundle\Menu;

  use Knp\Menu\FactoryInterface;
  use Symfony\Component\HttpFoundation\Request;

  class MenuBuilder
  {
    private $factory;

    /**
     * @param FactoryInterface $factory
     */
    public function __construct(FactoryInterface $factory)
    {
        $this->factory = $factory;
    }

    public function createBreadcrumbMenu(Request $request)
    {
        $menu = $this->factory->createItem('root');
        // cet item sera toujours affiché
        $menu->addChild('Home', array('route' => 'Acme_homepage'));

        // crée le menu en fonction de la route
        switch($request->get('_route')){
            case 'Acme_create_post':
                $menu
                    ->addChild('label.create.post')
                    ->setCurrent(true)
                    // setCurrent est utilisé pour ajouter une classe css "current"
                ;
            break;
            case 'Acme_list_post':
                $menu
                    ->addChild('label.list.post')
                    ->setCurrent(true)
                ;
            break;
            case 'Acme_view_post':
                $menu->addChild('label.list.post', array(
                    'route' => 'Acme_list_post'
                ));
                
                $menu
                    ->addChild('label.view.post')
                    ->setCurrent(true)
                    ->setLabel($request->get('label'))
                    // le paramètre "label" doit être passé dans votre controller
                    // avec $request->attributes->set('label','Mon libellé');
                ;
            break;
            case 'Acme_add_comment_on_post':
                $menu->addChild('label.list.post', array(
                    'route' => 'Acme_list_post'
                ));
                
                $menu
                    ->addChild('label.view.post', array(
                        'route' => 'Acme_view_post',
                        'routeParameters' => array('slug' => $request->get('slug'))
                        /* le paramètre "slug" est un paramètre de la route
                           Acme_add_comment_on_post. Si votre route n'a pas de paramètre, vous
                           devrez utiliser la méthode $request->attributes->set()
                        */
                    ))
                    ->setLabel($request->get('label'))
                ;
                $menu
                    ->addChild('label.add.comment')
                    ->setCurrent(true)
                ;                    
            break;            
        }

        return $menu;
    }
}

Enregistrer les services

Vous pouvez maintenant enregistrer le menu en tant que service. Vous devrez également créer un service pour le constructeur de menu.

# src/Acme/MainBundle/Resources/config/services.yml
services:
    # le service de constructeur de menus. Peut servir à créer plusieurs menus
    acme_main.menu_builder:
        class: Acme\MainBundle\Menu\MenuBuilder
        arguments: ["@knp_menu.factory"]

    # votre menu
    acme_main.menu.breadcrumb:
        class: Knp\Menu\MenuItem
        factory_service: acme_main.menu_builder # le service du constructeur vu juste au-dessus
        factory_method: createBreadcrumbMenu # la méthode appelée pour générer le menu
        arguments: ["@request"]
        scope: request
        tags:
            - { name: knp_menu.menu, alias: breadcrumb } # L'alias est utilisé dans le template

Afficher le menu

Ajoutez le menu dans votre template :

{{ knp_menu_render('breadcrumb',{'currentAsLink':false}) }}

L’option “currentAsLink” peut être supprimée si vous voulez que l’item courant soit un lien. Dans ce cas, n’oubliez pas d’ajouter l’argument “route” dans la méthode “addChild” de votre MenuBuilder.

Pour terminer, comme vous utilisez des clés comme libellés de vos menus (ex “label.view.post”), vous devez surcharger le template par défaut, comme c’est expliqué dans la documentation du bundle.

Et bien sûr, n’oubliez pas d’ajouter les traductions.

Avec un peu de CSS, voici le résultat :

Breadcrumb menu

Share Button

2 thoughts on “Comment créer un fil d’Ariane avec le KnpMenuBundle

  1. Merci pour cet article ! En revanche une petite question, comment faîtes-vous pour définir le séparateur entre vos liens SVP ?
    Par exemple “>” ou “|”.

    • Bonjour, nous les ajoutons en css après chaque li :

      div.forumBreadcrumb{
          ul li{
              display     : inline-block;
              &:after{
                  content : ">";
              }
              &:last-child:after{
                  content : "";
              }
          }
      }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Protected by WP Anti Spam