Qu’est-ce que Twig ?
Twig est un moteur de gabarit développé en PHP inclus par défaut avec le framework Symfony 5.
Mais PHP est déjà un moteur de gabarit : pourquoi devrions-nous utiliser et apprendre un moteur de gabarit supplémentaire ?
Le langage PHP qui était un moteur de gabarit à ses débuts est maintenant devenu un langage complet capable de supporter la programmation objet, fonctionnelle et impérative.
L'intérêt principal d'un moteur de gabarit est de séparer la logique de sa représentation. En utilisant PHP, comment définir ce qui est de la logique et ce qui est de la représentation ?
Pourtant, nous avons toujours besoin d'un peu de code dynamique pour intégrer des pages web :
pouvoir boucler sur une liste d'éléments ;
pouvoir afficher une portion de code selon une condition ;
ou formater une date en fonction de la date locale utilisée par le visiteur du site...
Voici pourquoi Twig est plus adapté que le PHP en tant que moteur de gabarit :
il a une syntaxe beaucoup plus concise et claire;
par défaut, il supporte de nombreuses fonctionnalités utiles, telles que la notion d'héritage ;
et il sécurise automatiquement vos variables.
Éléments de syntaxe
Twig supporte nativement trois types de syntaxe :
{{ ... }}
permet l'affichage d'une expression ;{% ... %}
exécute une action ;{# ... #}
n'est jamais exécuté, c'est utilisé pour des commentaires.
Voici un premier exemple de gabarit pour bien comprendre la différence :
{# Ceci est un exemple de gabarit Twig #}
set collection = [1, 2, 3]
for item in collection
{{ item }}
endfor
{#
Va afficher seulement ce code HTML:
<ul>
<li>1</li><li>2</li><li>3</li>
</ul>
#}
Revenons à notre application Symfony précédente, nous voulions absolument afficher un "Hello world" de toutes les façons possibles et imaginables . Eh bien, voici comment utiliser un gabarit Twig dans un contrôleur Symfony :
<?php
// src/Controller/HelloController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HelloController extends AbstractController
{
/**
* Hello world, avec Twig cette fois :)
*
* @Route("/hello/{name}", name="hello")
*/
public function hello($name)
{
$this->render('hello.html.twig', ['name' => $name]);
}
}
Et le gabarit Twig correspondant :
{# /templates/hello.html.twig #}
Hello {{ name }}!
Quelques explications supplémentaires ne seraient pas de refus, n'est-ce pas ?
Par défaut, le framework Symfony ira chercher les gabarits dans son dossier templates.
La fonction render prend en paramètre le chemin vers le gabarit et un tableau de paramètres.
Les paramètres sont disponibles dans le gabarit.
D'accord, mais pourquoi n'appelle-t-on pas le template tout simplement hello.twig ?
Pourquoi devrait-on limiter nos templates aux pages HTML ? Par exemple, si l'on a besoin de manipuler un fichier XML - disons un flux RSS -, nous pouvons tout à fait utiliser Twig pour cela, et nous nommerons le fichier flux.rss.twig, par exemple.
Opérateurs
On retrouve tous les opérateurs dans Twig, tous décrits dans la documentation officielle.
for key, element in elements
if loop.index % 2
Element pair
else
Element impair
endif
else
Il n'y a aucun élément à afficher.
endfor
Par exemple :
les opérateurs mathématiques (+, -, /, %, //, *, **, %) ;
les opérateurs de logique (and, or, not...) ;
les opérateurs de comparaison (==, !=, <, >, >=...) ;
des opérateurs plus utilitaires (~, ?:...).
Tags
Les tags sont des éléments de langage propres à Twig.
Voici la liste des tags les plus utilisés :
block : définit un espace surchargeable.
do, if, else,for, (with) : éléments de langage identiques à PHP.
import : permet d'importer un fichier comprenant des macros.
set : permet de définir une ou plusieurs variables.
spaceless : supprime tous les espaces entre les tags HTML.
verbatim : ne sera pas pris en compte par Twig.
Étendez votre utilisation de Twig : macros, fonctions et filtres
Il est possible d'étendre Twig avec trois types d'extensions différentes : les fonctions, les filtres et les macros.
Les fonctions et filtres à connaître
Une fonction peut changer la valeur d'une variable et peut avoir un ou plusieurs paramètres :
for i in range(1, 10)
{{ i }}
endfor
{# Twig supporte les paramètres nommés #}
{{ renderWithOptions(foo = 'foo', bar = 'bar', baz = 'baz') }}
Parmi la liste des fonctions disponibles nativement, voici les plus utilisées :
constant : appelle une constante PHP.
include : retourne le rendu d'un fichier.
dump : appelle la fonction dump (disponible uniquement dans Symfony).
Là où une fonction peut changer la valeur d'une variable, un filtre change seulement son affichage. On utilise l'opérateur "pipe" |
pour appliquer un filtre. Les filtres peuvent être chaînés.
{{ 'foo'|capitalize|reverse }} {# "Oof" #}
Par défaut, Twig protège et échappe toute valeur, donc s'il vous faut afficher un contenu HTML ou Javascript, utilisez le filtre e ou raw (aucune protection appliquée).
{{ '<h1>Foo</h1>' }} {# affichera "<h1>Foo</h1>" dans le navigateur #}
{{ '<h1>Bar</h1>'|raw }} {# affichera "Bar" tel qu'attendu #}
Parmi la liste des filtres disponibles nativement, voici les plus utilisés :
date : permet de formater une date.
first : affiche le premier élément.
last : affiche le dernier élément.
length : affiche la longueur d'un tableau, d'une chaîne de caractères.
number_format : permet de formater un nombre.
Avec tous ces fonctions et filtres, vous devriez déjà pouvoir faire de très belles intégrations, mais il y a mieux, une fonctionnalité de Twig particulièrement utile. Regardez l'exemple qui suit :
set tableau, article = [1,2,3], {
'titre': 'Etendre Twig',
'contenu': 'Il est possible ...'
}
{{ tableau.2 ~ ' ' ~ tableau|first }} {# "2 1" #}}
{{ article.titre }} {# "Etendre Twig" #}
{{ article.contenu }} {# "Il est possible ..." #}
Que s'est-il passé ici ? Plusieurs choses :
Tout d'abord, un exemple d'utilisation de
~
: il est utilisé pour la concaténation de chaînes de caractères.Twig est capable, à l'aide de l'opérateur
.
, d'aller retrouver la clé d'un tableau, la propriété publique d'un objet ou même l'accesseur correspondant ! Par exemple si l'objet article avait eu une fonction publique getContenu(), elle aurait été appelée.
Créez vos propres fonctions et filtres
Pour ajouter vos propres filtres et fonctions, il faudra créer une extension Twig.
Une extension Twig est une classe qui permet de définir ses propres filtres et fonctions et qui implémente Twig_ExtensionInterface, mais on préférera étendre la classe abstraite AbstractExtension:
<?php
// src/Twig/AppExtension.php
namespace App\Twig;
use Some\LipsumGenerator;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
public function getFilters()
{
return [
new TwigFilter('price', [$this, 'filterPrice']),
];
}
public function getFunctions()
{
return [
// appellera la fonction LipsumGenerator:generate()
new TwigFunction('lipsum', [LipsumGenerator, 'generate']),
];
}
public function filterPrice($number, $decimals = 0)
{
$price = number_format($number, $decimals);
$price = $price . '€';
return $price;
}
}
Quelques explications s'imposent.
Tout d'abord, dans une extension Twig, on peut définir des filtres et/où des fonctions.
Ensuite, la déclaration se fait de la même façon : en premier argument, le nom du filtre (ou de la fonction) et en deuxième argument, l'appel à effectuer. Cet appel peut être une fonction de la classe que l'on référence avec $this, ou un appel à une autre classe.
Grâce à l'autoconfiguration des services, Symfony va automatiquement reconnaître qu'il s'agit d'une extension Twig et rendre disponibles le filtre "price" et la fonction "lipsum".
Voici un exemple d'utilisation de ces extensions nouvellement créées :
set somePrice = 10.7567
{{ somePrice|price(2) }} {# "10.75€" #}
{{ lipsum() }}
{#
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam semper turpis et diam imperdiet, molestie scelerisque diam congue...
#}
Créez vos propres macros
Il reste un dernier type d'extension à présenter qui répond à un besoin plus spécifique. On définit une macro comme étant "une petite routine informatique pour automatiser une tâche répétitive".
Par exemple, imaginons que vous souhaitiez implémenter le système d'alertes du framework CSS Bootstrap4.
Basiquement, en fonction du type d'erreur, seule une classe CSS va changer :
class="alert alert-primary" role="alert"
Message d'alerte principal
class="alert alert-success" role="alert"
Message de succès
class="alert alert-danger" role="alert"
Message d'erreur
class="alert alert-warning" role="alert"
Message d'avertissement
<!-- ... et bien d'autres -->
Voilà ce que vous pourriez faire en définissant une macro Twig :
{# /templates/macros/alertes.html.twig #}
macro alert(message, type)
class="alert alert-{{ type }}" role="alert"
{{ message }}
endmacro
Et son utilisation dans un template Twig (n'oubliez pas d'importer la macro !) :
{# /templates/exemple.html.twig #}
import 'alertes.html.twig' as utils
{{ utils.alert('Attention!', 'warning') }}
{{ utils.alert('Erreur fatale!', 'danger') }}
Vous voilà entièrement équipé pour étendre Twig selon vos besoins les plus fous !
Maîtrisez l’héritage de gabarit
Tout au début ce de chapitre, je vous disais que l'héritage de gabarit était l'une des fonctionnalités les plus utiles de Twig.
Qu'est-ce qu'un héritage de gabarit ?
Définir des blocks et utiliser la fonction parent() permet de customiser et de rendre réutilisables les gabarits, comme un héritage de classe permet de redéfinir le comportement d'une classe parent.
Par exemple, le template parent.html.twig :
{# /templates/parent.html.twig #}
block titre Parent endblock
block stylesheets
rel="stylesheet" href="styles.css" type="text/css" media="all"
endblock
block contenuPrincipal
Contenu du body
endblock
block javascripts endblock
Et voici la page "Product" qui utilise "Parent" :
{# /templates/produit.html.twig #}
extends 'parent.html.twig'
block titre Page Produit endblock
block stylesheets
{{ parent() }}
rel="stylesheet" href="produit.css" type="text/css" media="all"
endblock
block contenuPrincipal
Ceci est la page Produit!
endblock
block javascripts
src="build/produit.js"
endblock
Rendra le fichier HTML suivant à l'utilisateur :
Page Produit
rel="stylesheet" href="styles.css" type="text/css" media="all"
rel="stylesheet" href="produit.css" type="text/css" media="all"
Ceci est la page Produit!
src="build/produit.js"
</body>
</html>
Pour résumer ce que nous avons fait ici :
nous avons rendu le titre de la page HTML configurable: par défaut, sa valeur est "Parent" si le bloc "titre" n'est pas surchargé ;
nous avons rendu le corps de la page configurable, et par défaut, il est vide ;
nous avons rendu l'ajout de fichier CSS et Javascript configurable : dans la page Produit, nous avons souhaité conserver l'appel au fichier CSS "styles.css" à l'aide de la fonction parent().
En résumé
Twig est un moteur de gabarit intégré au framework Symfony, avec une syntaxe claire et sécurisée par défaut.
Les fonctions sont utiles pour l'intégration de vos pages, par exemple dump dont nous avons parlé dans un chapitre précédent.
Les filtres sont utiles lorsque vous souhaitez changer l'affichage de vos données, et ils peuvent être chaînés.
Les macros permettent d'automatiser toutes les tâches d'intégration répétitives qui ne nécessitent aucun calcul : il faudra importer la macro dans chaque gabarit où vous en aurez besoin.
Il est possible de créer ses propres fonctions et filtres grâce à l'autoconfiguration du container de services. Il suffira de créer une classe qui étend AbstractExtension et les extensions seront disponibles dans vos gabarits.
Enfin, Twig supporte l'héritage : ceci permet de mettre les blocs de vos pages communes (haut de page, bas de page) dans les gabarits parents et de seulement surcharger des parties spécifiques du gabarit. L'intégration devient plus facile, car il y a moins de gabarits à maintenir et ils sont plus configurables.
Dans le prochain chapitre, vous découvrirez comment interagir avec vos utilisateurs par le biais de formulaires et ce sera l'occasion d'utiliser Twig dans un exercice pratique. À tout de suite !