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/"+monCP+"";
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 😉
Salut, merci pour tes explications intéressantes.
Je me suis retrouvé dans la même situation aujourd’hui !
Finalement, après quelques recherches, il me semble que le bundle FOSJsRoutingBundle permet de résoudre très simplement le problème !
Bonne continuation,
Marc
Bonjour,
Oui, il permet cela mais compare tout ce que tu ajoute, le fait qu’il te faille en plus ajouter un fichier javascript en plus dans ta page etc etc, et ma solution :
Si c’est l’escape de twig qui pose problème, il existe la fonction raw.
http://twig.sensiolabs.org/doc/filters/raw.html
Bonjour,
Ce si n’était que l’escape, je n’aurais pas fait tout un article là dessus 😉
Pourtant, j’avais le même problème et grâce à ton article je m’en suis sorti ! Un grand merci.
Mais je n’ai pas eu à faire la seconde partie du fait du filtre raw:
var url = "{{ path('my_route', {'id': "0000" }) | replace({'0000': '"+id+"'}) | raw }}";
Dans mon cas, j’utilise un placeholder numérique car ma route requiert
id="\d+"
.Mais le problème d’escape a été totalement résolu par raw.
Bonne solution, devant le même problème, j ‘avais trouvé une solution beaucoup plus moche, à savoir générer les routes dans le controller, avec en paramètre des valeurs ‘bidons’, passer la route à la vue en paramètre, et remplacer dans le code javascript par les valeurs une fois connues…
Grâce à vous j’ai pu me débloquer d’une manière plus élégante, Merci