Aller au contenu principal

Le moteur de templates Twig

Comprendre en détail Twig, le moteur de templates utilisé par Symfony pour générer le HTML des pages.


Notions théoriques

Rappel : à quoi sert Twig

Dans la séance sur le routage et les vues, vous avez déjà utilisé Twig sans le savoir vraiment expliqué : la méthode render() d'un contrôleur affiche un fichier .html.twig.

Twig est le moteur de templates de Symfony. Un moteur de templates permet d'écrire des fichiers qui ressemblent à du HTML, mais qui peuvent aussi contenir des variables PHP et de la logique simple (conditions, boucles), sans mélanger tout le code PHP au milieu du HTML.

info

Séparer le HTML (dans les templates Twig) du code métier (dans les contrôleurs et les entités) rend l'application plus facile à maintenir. Un intégrateur peut modifier l'apparence d'une page sans toucher au code PHP.

La syntaxe Twig

Twig utilise deux types de balises principales :

  • {{ ... }} : pour afficher une valeur (une variable, le résultat d'un filtre, etc.)
  • {% ... %} : pour exécuter une instruction (condition, boucle, héritage, bloc...)

Afficher une variable :

<p>Bonjour {{ prenom }} !</p>

Une condition :

{% if joueurs|length > 0 %}
<p>Il y a des joueurs inscrits.</p>
{% else %}
<p>Aucun joueur pour le moment.</p>
{% endif %}

Une boucle :

<ul>
{% for joueur in joueurs %}
<li>{{ joueur.name }}</li>
{% endfor %}
</ul>
attention

Dans une boucle Twig, on accède à une propriété d'objet avec la notation pointée (joueur.name), même si en PHP il s'agirait d'une méthode getName(). Twig essaie automatiquement la propriété, puis le getter correspondant.

L'héritage de templates

Sans héritage, il faudrait recopier le header, le menu et le footer dans chaque template. L'héritage de Twig évite cette duplication.

Le principe : on crée un template parent (souvent base.html.twig) qui contient la structure commune, avec des blocs vides à des endroits précis. Les templates enfants héritent du parent avec {% extends %} et remplissent uniquement les blocs qui les intéressent.

Le template parent base.html.twig :

<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Mon Jeu{% endblock %}</title>
</head>
<body>
<header>Mon Jeu</header>

<main>
{% block content %}{% endblock %}
</main>

<footer>Copyright {{ "now"|date("Y") }}</footer>
</body>
</html>

Un template enfant :

{% extends 'base.html.twig' %}

{% block content %}
<h1>Liste des joueurs</h1>
{% endblock %}

Le résultat final combine automatiquement la structure de base.html.twig avec le contenu du bloc content défini dans le template enfant. Le header et le footer sont hérités sans être réécrits.

Les filtres Twig usuels

Un filtre transforme une valeur avant de l'afficher. On l'applique avec le symbole | (pipe).

FiltreRôleExemple
upperMet en majuscules{{ name|upper }}
lowerMet en minuscules{{ name|lower }}
dateFormate une date{{ createdAt|date("d/m/Y") }}
lengthCompte les éléments{{ joueurs|length }}
defaultValeur de secours si vide{{ description|default("Aucune description") }}

Les fonctions Twig usuelles

Une fonction Twig, contrairement à un filtre, s'écrit avec des parenthèses, comme en PHP.

FonctionRôleExemple
path()Génère une URL à partir du nom d'une route{{ path('game_show', {id: 1}) }}
asset()Génère le chemin vers un fichier statique (CSS, JS, image){{ asset('css/style.css') }}
Bonne pratique - Toujours utiliser path() plutôt qu'une URL écrite en dur

Écrire <a href="/game/1"> fonctionne, mais si la route change de chemin dans le contrôleur, tous les liens écrits en dur cassent. {{ path('game_show', {id: 1}) }} régénère toujours l'URL correcte à partir du nom de la route, même si le chemin change.

Exemple de mise en application

Reprenons le projet my_game et son GameController. Nous allons créer un template parent base.html.twig, puis faire hériter la page d'affichage d'un jeu de ce template.

Le fichier templates/base.html.twig :

<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Mon Jeu{% endblock %}</title>
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
</head>
<body>
<header>
<a href="{{ path('home') }}">Accueil</a>
</header>

<main>
{% block content %}{% endblock %}
</main>
</body>
</html>

Le fichier templates/game/show.html.twig hérite de base.html.twig :

{% extends 'base.html.twig' %}

{% block title %}Détails du jeu{% endblock %}

{% block content %}
<h1>{{ name|upper }}</h1>

<p>{{ description|default("Pas de description pour ce jeu.") }}</p>

{% if joueurs|length > 0 %}
<ul>
{% for joueur in joueurs %}
<li>{{ joueur.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>Aucun joueur inscrit sur ce jeu.</p>
{% endif %}
{% endblock %}

La page affichée combine la structure de base.html.twig (header avec le lien vers l'accueil) et le contenu spécifique défini dans le bloc content de show.html.twig.

Test de mémorisation/compréhension


À quoi servent les doubles accolades {{ }} en Twig ?


À quoi servent les balises {% %} en Twig ?


Quelle instruction permet à un template d'hériter d'un template parent ?


Que permet de faire un bloc {% block content %}...{% endblock %} ?


Quel filtre permet de mettre un texte en majuscules ?


Quel filtre permet d'afficher une valeur de secours si une variable est vide ?


À quoi sert la fonction path() en Twig ?


À quoi sert la fonction asset() en Twig ?


Dans une boucle {% for joueur in joueurs %}, comment accède-t-on à la propriété name de chaque joueur ?


TP pour réfléchir et résoudre des problèmes

Votre défi pour aujourd'hui consiste à créer un template parent commun à toutes les pages du projet my_game, et à l'utiliser pour la page d'affichage d'un joueur.

Étape 1 — Créer le template parent base.html.twig

Créez le fichier templates/base.html.twig avec un bloc title (valeur par défaut "Mon Jeu") et un bloc content vide.


Bonne pratique - Toujours prévoir un contenu par défaut dans les blocs

Donner une valeur par défaut à un bloc comme title évite d'avoir une page sans titre si un développeur oublie de redéfinir le bloc dans un template enfant.

Étape 2 — Faire hériter le template player/show.html.twig de base.html.twig

Le fichier templates/player/show.html.twig doit maintenant hériter de base.html.twig au lieu de contenir tout le HTML lui-même.

Étape 3 — Afficher le nom du joueur en majuscules

Dans le bloc content de player/show.html.twig, la variable name est transmise par le contrôleur. Affichez-la en majuscules à l'aide d'un filtre Twig.


Bonne pratique - Faire les transformations d'affichage dans le template, pas dans le contrôleur

Mettre un texte en majuscules pour l'affichage relève de la présentation, pas de la logique métier. Utiliser le filtre upper dans le template plutôt que strtoupper() dans le contrôleur garde le contrôleur concentré sur son rôle : préparer les données.

Étape 4 — Ajouter un lien vers la liste des joueurs avec path()

Dans le bloc content, ajoutez un lien qui pointe vers la route nommée player_index, en utilisant la fonction path().

📌 Une solution