Mots-clé : template engine

symfony 2 : isset et null avec Twig

Une des questions qu’on cherche le plus souvent avec Twig, c’est comment vérifier si une variable existe ou pas ?

La réponse se fait en trois étapes :

  1. Vérifier si une variable existe (= si elle a été initialisée) :
    En php, c’est isset().
    En Twig, c’est defined.
    Exemple de code Twig :

    {% if app.user is defined %}
    Code html
    {% endif %}
  2. Vérifier si une variable n’est pas null (= elle est définie, mais contient la valeur null) :
    En php, c’est !is_null().
    En Twig, c’est is not null.
    Exemple de code Twig :

    {% if app.user is not null%}
    Code html
    {% endif %}
  3. Vérifier si une variable existe et qu’elle n’est pas null (= elle est définie, et contient autre chose que la valeur null) :
    En php, c’est isset() && (!is_null()).
    En Twig, c’est is defined and is not null.
    Exemple de code Twig :

    {% if app.user is defined and app.user is not null%}
    Code html
    {% endif %}

En espérant que cela aide du monde !

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 😉