KnpMenuBundle Corrspondance avec le menu en cours

Le problème que concerne cet article est encore lié au KnpMenuBundle et fait suite à un précédent article sur le KnpMenuBundle et les fils d’Ariane.

Le problème que nous sommes nombreux à rencontrer, est que ce bundle ne fait pas la correspondance de route avec paramètre(s).

une route en Symfony2 peut être associé à un nom.
Par exemple si l’on utilise les annotations dans les controller, on peux avoir quelque chose comme ça :

class amazingController extends Controller {
/**
 * @Route("/a-funky-route" name="_a_funky_route")
 */
public function getHeadachesAction()
{
    //...
}

/**
 * @Route("/say-my-name/{myName}" name="_say_my_name", defaults={"myName"="Arthur Dent"})
 */
public function sayMyNameAction($myName)
{
    echo $myName; // <- ça c'est pas bien, c'est juste pour l'exemple ;)
}

Et si l’on configure notre Menu Builder comme ce-ci :

class Builder extends ContainerAware {
//...
 public function menu(FactoryInterface $factory, array $options) {

        $menu = $factory->createItem('menu');

            $menu->addChild('Menu 1', array('route' => '_a_funky_route'));
            $menu->addChild('Menu 2', array('route' => '_say_my_name', 'routeParameters' => array('myName' => null)));

        return $menu;
    }
}

Dans ce cas de configuration, KnpMenuBundle est incapable de faire correspondre une url http://site.fr/say-my-name/Pedro avec la route _say_my_name.

C’est là où l’on va ajouter un peu de magie dans tout ça !

En premier lieu, le code de @merk que l’on va un peux améliorer.

namespace WaldoMyFunTaStikBundleVoter;

use KnpMenuItemInterface;
use KnpMenuMatcherVoterVoterInterface;
use SymfonyComponentDependencyInjectionContainerInterface;

class RequestVoter implements VoterInterface {
   /**
     * @var SymfonyComponentDependencyInjectionContainerInterface
     */
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * Checks whether an item is current.
     *
     * If the voter is not able to determine a result,
     * it should return null to let other voters do the job.
     *
     * @param ItemInterface $item
     * @return boolean|null
     */
    public function matchItem(ItemInterface $item)
    {

        /* @var $request SymfonyComponentHttpFoundationRequest */
        $request = $this->container->get('request');

        if ($item->getUri() === $request->getRequestUri()) {
            return true;
        }

        // C'est ici que l'on vérifie que la route en cours d'utilisation est bien la même
        // que celle contenu dans l'item que nous passe le KnpMenuBundle
        if ($item->getExtra('routes') !== null && in_array($request->attributes->get('_route'), $item->getExtra('routes'))) {
            return true;
        }

        return null;
    }
}

Ce bout de code Xml est à placer dans config/service.xml

<service id="waldo.voter.request" class="WaldoMyFunTaStikBundleVoterRequestVoter">
  <tag name="knp_menu.voter" />
  <argument type="service" id="service_container" />
</service>

Besoin de comprendre ?

$item->getExtra('routes') contient un tableau composé de route(s) définis dans le builder de menu : $menu->addChild('Menu 1', array('route' => '_a_funky_route'));.
Et $request->attributes->get('_route') donne le nom de la route qui correspond à l’url de la page que l’utilisateur a appelé.
On ne se base plus sur les URLs générés, comme le fait le KnpMenuBundle par défaut, mais sur le nom des routes qui ne change jamais, paramètres ou non.

Tagués avec :
Publié dans Symfony2, Symfony2 v2.1

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*