Symfony 2: générer une url dynamique dans twig dans du code javascript

J’ai eu à faire face à un problème que vous allez très certainement rencontrer si vous faites Symfony2. Dans les fichiers de template, vous allez sûrement mettre du Javascript. Exemple :

<script type="text/javascript">
<!--
function verif_formulaire(){
    window.location = '/test/mon/url/';
}
-->
</script>

Maintenant, si on essaie de faire ça en Twig, c’est simple. Je ne m’attarderai que sur l’URL :

    window.location = '{{ path('my_path') }}';

Supposons que votre path nécessite un paramètre, par exemple, dans mon cas, le code postal :

    window.location = '{{ path('my_path', {'cp': "13480" }) }}';

Facile. Mais supposons que votre code en JavaScript veuille le générer dynamiquement :

/* récupération de la valeur quelque part : */
var monCP = document.getElementById('cp').value;
window.location = ="{{ path('hqf_pizzas_searchpage', {'cp': monCP }) }}";

Eh bien ça ne fonctionnera pas sur Symfony2. Ne cherchez pas c’est comme ça. Vous aurez une erreur. L’erreur, pour reprendre mon code, était :

Variable "monCP" does not exist in HQFPizzasBundle:Default:index.html.twig at line 11

Voici la solution que j’ai trouvée : je reprends ma configuration et il vous suffira de l’adapter à vos besoins. Dans mon fichier de routing src/HQF/Bundle/PizzasBundle/Resources/config/routing.yml, j’ai ce path qui nécessite le paramètre cp :

hqf_pizzas_searchpage:
    pattern:  /search/cp/{cp}
    defaults: { _controller: ... }

L’objectif est de ressortir le path avec un '%s' dedans, de manière à pouvoir avoir une URL qui ressemble à :

/search/cp/%s

Ainsi, il suffira juste après d’utiliser la fonction Twig ‘format‘ et d’y passer la variable JavaScript, par exemple monCP.
Ainsi cet ordre twig:

{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | format('monCP') }}

génèrera ceci :

/search/cp/monCP

L’objectif final est de sortir du vrai code JavaScript qui ressemble à :

window.location="/search/cp/"+monCP+"";

Donc si veut y arriver, le mélange code Twig + JavaScript, avec les guillemets, devrait être :

window.location = "{{ path('hqf_pizzas_searchpage', {'cp': "%s" })  | format('"+monCP+"') }}"

Seulement, problème : il escape tout ! Le code généré sera ainsi :

window.location ="/pizzas/search/cp/%25s";

Solution : pour que twig n’échappe pas le texte, il faut créer son propre filtre Twig !

Voici les étapes à suivre :

J’ai crée mon fichier src/HQF/Bundle/PizzasBundle/Twig/UrlDecodeExtension.php dans lequel j’ai mis ce code :

<?php

namespace HQF\Bundle\PizzasBundle\Twig;

class UrlDecodeExtension extends \Twig_Extension
{
    public function getFilters()
    {
        return array(
            'url_decode' => new \Twig_Filter_Method($this, 'urlDecode'),
        );
    }

    public function urlDecode( $url )
    {
        return urldecode( $url );
    }

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

Ensuite, je l’ai enregistré dans les services.
C’est dans le fichier src/HQF/Bundle/PizzasBundle/Resources/config/services.yml les lignes :

services:
    cme.twig.url_decode_extension:
        class: HQF\Bundle\PizzasBundle\Twig\UrlDecodeExtension
        tags:
            - { name: twig.extension }

A partir de là le filtre url_decode fonctionne. Il suffit de faire le code qui suit :

/* récupération de la valeur quelque part */
var monCP = document.getElementById('cp').value;
window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode | format('"+monCP+"') | raw }}";

Afin de générer cela en JavaScript :

/* récupération de la valeur quelque part */
var monCP = document.getElementById('cp').value;
window.location ="/pizzas/search/cp/"+monCP+"";

Ce qui est du code JavaScript parfaitement exécutable.

Voici les explications pas à pas :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) }}";

Génère cela :

window.location ="/pizzas/search/cp/%25s";

C’est échappé, et il ne le faut pas ! Donc ajouter le filtre url_decode :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode }}";

Là le résultat sera celui attendu :

window.location ="/pizzas/search/cp/%s";

Ensuite on y ajoute la fonction format afin d’y ajouter la variable JavaScript :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode | format('"+monCP+"') }}";

Mais là encore le résultat sera échappé :

window.location ="/pizzas/search/cp/&quot;+monCP+&quot;";

Donc il faut lui dire de ressortir le résultat final au format raw :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode | format('"+monCP+"') | raw }}";

Et le résultat de sortie sera (enfin !) parfait :

window.location ="/pizzas/search/cp/"+monCP+"";

Cet article est un mélange de l’explication de création des filtres Twig, ici, et de la question qui ressemble très fortement à la mienne sur stackoverflow, ici.

En espérant que cela serve à quelqu’un, un jour 😉

6 comments

Poster un commentaire

Vous devriez utiliser le HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.