3 votes

Symfony2 routage dynamique vers une action de contrôleur avec plusieurs paramètres optionnels

Je suis encore assez nouveau sur Symfony2 et j'ai du mal à comprendre le problème suivant.

J'ai une Action de navigation principale dans un contrôleur avec les routes suivantes définies (dans le contrôleur) :

/**
 * @Route("/browse")
 * @Route("/browse/{page}")
 * @Route("/browse/c/{category}/{categoryName}")
 * @Route("/browse/c/{category}/{categoryName}/{page}")
 * @Route("/browse/c/{category}/b/{brand}/{page}")
 * @Route("/browse/b/{brand}")
 * @Route("/browse/b/{brand}/{page}")
 * @Template()
 */
public function browseAction($category = 0, $page = 1, $brand = 0) {

Le routage ci-dessus fonctionne sans problème.

Le problème est de générer des URL à partir de vues twig ou des aides de vue.

J'aurais aimé pouvoir faire ce qui suit dans une aide de vue :

{{ url('browse', {'brand': '123'}) }}

Cela fonctionne bien avec ce qui suit dans routing.yml :

browse:
  pattern: /browse/b/{brand}
  defaults: { _controller: MyCoreBundle:Browse:browse } 

J'ai ensuite essayé :

browse:
  pattern: /browse/b/{brand}
  pattern: /browse/c/{category}/b/{brand}
  defaults: { _controller: MyCoreBundle:Browse:browse } 

Mais seul le dernier modèle semble s'appliquer et essayer d'utiliser ce qui suit provoquerait une erreur :

{{ url('browse', {'brand': '123'}) }}

Je réalise que je peux créer plusieurs routes individuelles dans routing.yml qui ont des noms uniques. Mais cela signifie qu'en fonction des variables qui seront utilisées, je dois spécifier un nom de route différent, ce qui deviendrait très rapidement compliqué.

J'ai également essayé :

browse:
  pattern: /browse/c/{category}/b/{brand}/{page}
  defaults: { _controller: MyCoreBundle:Browse:browse } 

Avec :

{{ url('browse', {'brand': '123', 'category':'', 'page': '1'}) }}

Mais cela a provoqué une erreur indiquant que la catégorie n'était pas dans le bon format.

Est-ce que je rate quelque chose ici ? Est-ce que quelqu'un pourrait me mettre sur la bonne voie ? Dois-je peut-être créer une extension twig qui peut prendre toutes les variables et construire l'URL en fonction de l'entrée ?

3voto

someuser Points 957

J'ai fini par créer une extension twig. C'est long et cela implique de passer les route_params et query_params mais cela fonctionne.

Donc dans mon contrôleur, j'avais besoin d'obtenir les paramètres de route et de requête :

$routeParams = $this->get('request')->attributes->get('_route_params');
$queryParams = $this->get('request')->query->all();

Ensuite, passez à la vue :

return array('products' => $products,  'mfdFacets' => $mfdFacets, 'routeParams' => $routeParams, 'queryParams' => $queryParams);

Ensuite, dans mon browse.html.twig j'appelle un aide de vue et passe les paramètres de route et de requête :

{% render controller("MyCoreBundle:Helper:menumfd", {'mfdFacets': mfdFacets, 'routeParams': routeParams, 'queryParams': queryParams }) %}

Ensuite, dans le contrôleur d'aide :

   /**
     * @Route("/helper/menu/module/mfd")
     * @Template()
     */
    public function menumfdAction($mfdFacets, $routeParams, $queryParams) {
    $manufacturers = $this->get("my.manufacturers")->makeNamedArray($mfdFacets);
    return array('manufacturers' => $manufacturers, 'routeParams' => $routeParams, 'queryParams' => $queryParams);
    }

Ensuite, dans la vue d'aide :

{% for mfd in manufacturers %}

    {{ mfd.name | raw }} ({{ mfd.count }})

{% endfor %}

Ensuite, la classe d'extension Twig (service ?) :

container = $container;
    }

    public function getFunctions() {
    return array(
        'mybrowseroute' => new \Twig_Function_Method($this, 'myBrowseRoute')
    );
    }

    public function myBrowseRoute($label, $value, $label2, $value2, $routeParams, $queryParams) {

    #print_r($routeParams);
    $route_array = array("category", "categoryName", "brand", "brandName");

    ## Value 1
    if (array_key_exists($label, $routeParams)) {
        $routeParams["$label"] = $value;
    } else {
        if (in_array($label, $route_array)) {
            $routeParams["$label"] = $value;
        }
    }
    if (array_key_exists($label, $queryParams)) {
        $queryParams["$label"] = $value;
    } else {
        if (!array_key_exists($label, $route_array) && !array_key_exists($label, $routeParams)) {
            $queryParams["$label"] = $value;
        }
    }

    ## Value 2
    if (array_key_exists($label, $routeParams)) {
        $routeParams["$label2"] = $value2;
    } else {
        if (in_array($label, $route_array)) {
            $routeParams["$label2"] = $value2;
        }
    }
    if (array_key_exists($label2, $queryParams)) {
        $queryParams["$label2"] = $value2;
    } else {
        if (!array_key_exists($label2, $route_array) && !array_key_exists($label2, $routeParams)) {
            $queryParams["$label2"] = $value2;
        }
    }

    ## Générer la chaîne d'URL

    $base_route = $this->container->get('router')->generate("browse");
    $routeString = $base_route;

    if (array_key_exists("category", $routeParams)) {
        $routeString .= "/c/" . $routeParams["category"];
    }
    if (array_key_exists("categoryName", $routeParams)) {
        $routeString .= "/" . urlencode($routeParams["categoryName"]);
    }
    if (array_key_exists("brand", $routeParams)) {
        $routeString .= "/b/" . $routeParams["brand"];
    }
    if (array_key_exists("brandName", $routeParams)) {
        $routeString .= "/" . urlencode($routeParams["brandName"]);
    }

    # Page
    $routeString .= '/1';

    $i = 1;
    foreach($queryParams as $qLabel => $qValue){
        if($i == 1){
            $routeString .= "?$qLabel=$qValue";
        } else {
            $routeString .= "&$qLabel=$qValue";
        }
        $i++;
    }

    return $routeString;
    }

    public function getName() {
    return 'my_router';
    }

}

Qui nécessite ce qui suit dans routing.yml :

browse:
      pattern: /browse
      defaults: { _controller: MyCoreBundle:Browse:browse } 

Et dans services.yml :

services:
    my.router:
    class: My\Bundle\ServiceBundle\Twig\Extension\MyRouterExtension
    arguments: ['@service_container']
    tags:
        - { name: twig.extension }

Si vous ne souhaitez passer qu'une paire clé-valeur dans l'aide de la vue, vous pouvez simplement utiliser :

{{ mfd.name | raw }} ({{ mfd.count }})

1voto

Emii Khaos Points 8933

Déboguez les routes et vous verrez que chaque annotation de route obtient un nom unique

php app/console router:debug

Vous pouvez définir des noms individuels avec le paramètre name dans l'annotation. Jetez un coup d'œil à la documentation.

@Route("/browse/{page}", name="browse_with_page") 

Créer une extension twig dédiée pour votre cas pourrait être une autre solution. Ou peut-être que ce billet de blog et les mentions pourraient être intéressants.

EDIT: vous pouvez réduire vos routes, car le paramètre page peut être facultatif.

@Route("/browse/c/{category}/{categoryName}")
@Route("/browse/c/{category}/{categoryName}/{page}")

est identique et peut être appelé des deux façons.

@Route("/browse/c/{category}/{categoryName}/{page}", defaults={"page" = 1})

Testez-le sur la console, la même route devrait correspondre.

php app:console router:match /browse/c/1/foobar
php app:console router:match /browse/c/1/foobar/2

-2voto

Andrew Points 806

Tout d'abord, ce n'est pas du routage dynamique. Il s'agit de routage statique avec plusieurs modèles. Il y a une différence distincte.

Deuxièmement, vous devrez créer une route spécifiquement définie pour chaque modèle, avec des noms uniques pour chacun. Cependant, vous pourriez définir par défaut le paramètre de page à 1 pour le rendre facultatif, réduisant ainsi le nombre de modèles définis.

Troisièmement, lorsque vous avez besoin de générer ces liens en twig, utilisez simplement le nom unique défini ci-dessus, avec les paramètres requis.

Enfin, avec votre dernier exemple, la catégorie est un paramètre requis, et vous passez une valeur vide. Cela créerait "/browse/c//b/123/1", ce qui est une route invalide pour Symfony2 à gérer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X