Apprendre Symfony 6 : Moteur de Template Twig

Twig est un moteur de template. Son rôle est de dynamiser les templates d'une application en exploitant les mécaniques de la programmation.

Icône de calendrier
Intermédiaire
13 chapitres

Twig : un moteur de template

Qu’est-ce qu’un moteur de templates ?

PHP peut être considéré comme un moteur de template car il permet de dynamiser les templates d’un site web en mélangeant du code PHP et du code HTML. Néanmoins, cela reste très verbeux et parfois peu adapté à nos besoins.

Un moteur de template propose ainsi un langage dédié à la création de nos interfaces, laissant PHP exclusivement en charge de la logique applicative.

Ce langage est, tout comme PHP, interprété côté serveur (exécution des boucles, conditions, fonctions, lecture des variables…) afin de retourner un fichier HTML statique au client.

Twig est un moteur de templates pour le langage PHP. Il a, à l’origine, été développé pour le framework Symfony.

Exemple de PHP traditionnel

copié !
<?php if count($articles) > 0) { ?>
	<?php foreach ($articles as $article) { ?>
		<article class="product">
			<h2 class="product-title"><?= htmlspecialchars($product['title']) ?></h2>
			<p class="product-description"><?= htmlspecialchars($product['description']) ?></p>
			<span class="product-date">Publié le <?= htmlspecialchars(date_format($product['published_at'], 'd/m/Y')) ?></span>
		</article>
	<?php } ?>
<?php } else { ?>
	<p>Aucun produit...</p>
<?php } ?>

Équivalent Twig

copié !
{% for article in articles %}
	<article class="product">
		<h2 class="product-title">{{ product.title }}</h2>
		<p class="product-description">{{ product.description }}</p>
		<span class="product-date">Publié le {{ product.published_at|date('d/m/Y') }}</span>
	</article>
{% else %}
	<p>Aucun produit...</p>
{% endfor %}

Un peu mieux n’est-ce pas ?!

Pourquoi utiliser un moteur de template ?

Un moteur de template vient avec son lot d’avantages et un inconvénient évident.

➕ Avantages

  • Des templates plus clairs et concis : Twig est moins verbeux que PHP et sa syntaxe est pensée pour être intuitive.
  • La sécurité : Twig échappe par défaut les variables à l’affichage (htmlspecialchars() automatiques).
  • Support de nombreuses fonctionnalités utiles : héritage, inclusions…
  • Une architecture qualitative : en utilisant un moteur de template comme Twig, il est impossible d’écrire du PHP dans nos templates, cela nous force ainsi à séparer la logique applicative de l’affichage.

➖ Inconvénient

  • L’apprentissage d’une nouvelle technologie.

En bref, Twig, c’est facile, beau, puissant et sécurisé !

Syntaxe (délimiteurs)

Ce moteur de template permet de dynamiser des pages web via tout un tas de fonctionnalités. Ces dernières sont exploitables au sein de délimiteurs. On distingue 3 types de délimiteurs :

  • Commentaires : comme tout langage, Twig propose une syntaxe pour écrire des commentaires au sein du code avec les délimiteurs {# Mon commentaire … #}. Contrairement aux commentaires HTML, ceux-ci n’apparaîtront pas dans la page générée.
  • Expressions (variables, fonctions et filtres) : l’affichage des variables, du résultat de fonctions ou encore de filtres se fera au sein des délimiteurs {{ expression }}.
  • Tags : les tags regroupent l’ensemble des mécanismes dynamiques de Twig. Il s’agit d’exécuter des structures de contrôle (conditions), structures itératives (boucles), mécaniques d’héritage et d’inclusions de template, etc. On les écrira au sein des délimiteurs {% tag %}.

Extension .html.twig

Tous les fichiers Twig porteront l’extension .html.twig.

Mise en page

Pour comprendre la mise en page avec Twig, il est important de comprendre les notions de layout, pages et partials.

Héritage

En Programmation Orientée Objet (POO), l’héritage consiste à hériter des propriétés et méthodes d’un élément.

Avec Twig, l’héritage consiste à hériter de la mise en page d’un parent.

L’héritage consiste à structurer ses templates de manière à les emboîter entre eux.

Cette mécanique est une implémentation des pseudo-frames en PHP, consistant à router notre application vers le fichier 📄 index.php en faisant varier un paramètre $_GET['page'], de sorte à ce que notre layout charge dynamiquement la page adaptée à la requête envoyée par le client.

L’héritage permet ainsi d’éviter la répétition du code de la mise en page globale commune à plusieurs pages. Mettre en place l’héritage consiste à :

  1. Créer un template parent qui va contenir le design global du site (= le layout, contenant les éléments communs à plusieurs pages).
  2. Création des templates enfants, qui vont hériter du parent, en y ajoutant un contenu spécifique et unique (= les pages).

Parent

Le parent est le template contenant les balises de premier niveau (html, head et body) et la mise en page globale d’un site web.

Par défaut, lorsque vous initialisez un projet Symfony, vous possédez déjà le fichier qui va jouer le rôle de template parent, il s’agit du fichier 📄 base.html.twig, situé à la racine du dossier 📁 templates. On l’appelle le « layout » de notre site.

base.html.twig
copié !
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>{% block title %}Welcome!{% endblock %}</title>
	</head>
	<body>
		{% block body %}{% endblock %}
	</body>
</html>

Notre attention va se porter sur les 2 blocs Twig {% block title %} et {% block body %}

Ici, le rôle de ces balises est de définir dans le layout des zones identifiées avec la syntaxe {% block nom_du_bloc %} ... {% endblock %}. Ces zones sont vouées à accueillir du contenu, défini au niveau du template enfant. Ici, le block body va inclure le contenu propre à chaque page.

Comme vous pouvez le constater, ces zones peuvent :

  • Être totalement vierges (la plupart du temps, comme c’est le cas du block body)
  • Déjà contenir des informations (parfois, comme c’est le cas du block title). Il y a 2 cas de figure pour lesquels on pourra mettre du contenu à l’intérieur des blocs définis par le parent :
    1. Si le template enfant ne va pas remplir cette zone, nous aurons alors une valeur par défaut.
    2. Si le template enfant souhaite récupérer la valeur du parent et y ajouter des informations spécifiques (on dit qu’il va le surcharger), alors on utilisera la fonction Twig parent() dans le template enfant pour récupérer les informations du parent.
Définir un title

Généralement sur un site web, le titre de notre page a la forme « Titre de la page - Nom du site ».

C’est une bonne habitude à prendre qui est bénéfique en termes de référencement, mais aussi pour l’expérience des utilisateurs des moteurs de recherche, qui vont pouvoir voir à quels sites correspondent les résultats de recherche.

Cela pourrait se faire en ajoutant le nom du site à la fin de chaque {% block title %}{% endblock %} des pages… mais cela serait maladroit !

{% block title %}Accueil - Nom site{% endblock %}
{% block title %}Formations - Nom site{% endblock %}

Pour éviter cela et respecter le principe du DRY, on isolera plutôt le nom du site dans le layout et les templates enfants ne viendront spécifier que le titre de la page :

base.html.twig
copié !
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>{% block title %}{% endblock %} - Nom site</title>
	</head>
	<body>
		{% block body %}{% endblock %}
	</body>
</html>
copié !
{% extends 'base.html.twig' %}

{% block title %}Titre de la page{% endblock %}

Enfants

Les enfants sont les templates que vous allez inclure dans votre parent pour venir y injecter du contenu.

Les templates retournés par une méthode de contrôleur avec la fonction render() sont des templates enfants. Ils vont venir remplir des blocs spécifiques d’une mise en page type définie dans le template parent, pour afficher une page complète.

On les retrouve dans des sous-dossiers situés dans 📁 templates/nom-sous-dossier. Le nom de ce sous-dossier correspond généralement au contrôleur qui va appeler les templates qu’il contiendra.

La ligne de commande symfony console make:controller nous avait créé un premier template enfant nommé 📄 index.html.twig par défaut.

index.html.twig
copié !
{% extends 'base.html.twig' %}

{% block title %}Hello AppController!{% endblock %}

{% block body %}
	<style>
		.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
	</style>

	<div class="example-wrapper">
		<h1>Hello {{ controller_name }}! ✅</h1>
		This friendly message is coming from:
		<ul>
			<li>Your controller at <code><a href="{{ 'C:/Symfony/demo/src/Controller/AppController.php'|file_link(0) }}">src/Controller/AppController.php</a></code></li>
			<li>Your template at <code><a href="{{ 'C:/Symfony/demo/templates/app/index.html.twig'|file_link(0) }}">templates/app/index.html.twig</a></code></li>
		</ul>
	</div>
{% endblock %}

Pour qu’un template hérite d’un parent, il va falloir lui indiquer avec le tag {% extends %}. Vous pouvez voir cette balise tout en haut du fichier. Ici, on rattache un enfant à son parent.

Utilisation de la fonction parent()

La fonction Twig parent() utilisée au sein d’un template enfant permet de récupérer le contenu du bloc parent portant le même nom.

parent.html.twig
copié !
{% block sidebar %}
	<p>Contenu du parent</p>
{% endblock %}
enfant.html.twig
copié !
{% block sidebar %}
	{{ parent() }} {# Récupère "<p>Contenu du parent</p>" #}
	<p>Contenu de l'enfant</p>
{% endblock %}

Héritage multiple

L’héritage multiple : quand bébé devient grand

Dans la vraie vie, un enfant peut devenir parent à son tour… et bien avec Twig, c’est pareil !

L’héritage multiple consiste à mettre en place une relation d’héritage de plus de 2 niveaux. Concrètement, cela consiste à définir qu’un enfant sera aussi parent d’un autre template.

Imaginons que vous souhaitez afficher des publicités sur certaines pages de votre site. Il peut être intéressant de créer un template qui va contenir les publicités et un autre qui ne les contiendra pas.

👴 Le grand-parent
base.html.twig
copié !
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>{% block title %}{% endblock %} - Nom site</title>
	</head>
	<body>
		{# Ici, mon header #}
		{% block body %}{% endblock %}
		{# Ici, mon footer #}
</body>
</html>
🧑 L'enfant (template sans pub)
layout_no_ads.html.twig
copié !
{% extends 'base.html.twig' %}

{% block body %}
	{% block content %}{% endblock %}
{% endblock %}
🧑 L'enfant (template avec pub)
layout_ads.html.twig
copié !
{% extends 'base.html.twig' %}

{% block body %}
	<aside class="ad">
		{# Ici, je place une publicité avant mon contenu #}
	</aside>
	{% block content %}{% endblock %}
	<aside class="ad">
		{# Ici, j'en place une après #}
	</aside>
{% endblock %}
👶 Le petit-enfant (page sans pub)
page1.html.twig
copié !
{% extends 'app/layout_no_ads.html.twig' %}

{% block title %}Page 1{% endblock %}

{% block content %}
	<h1>Page 1</h1>
	<p>...</p>
{% endblock %}
👶 Le petit-enfant (page avec pub)
page2.html.twig
copié !
{% extends 'app/layout_ads.html.twig' %}

{% block title %}Page 2{% endblock %}

{% block content %}
	<h1>Page 2</h1>
	<p>...</p>
{% endblock %}

En résumé

  • Un template parent contient des blocs qui vont découper la page en zones.
  • Les templates enfants rappellent ces blocs par leur nom afin d’y injecter du contenu.

Inclusion

Sur un site web, on trouve généralement des éléments communs d’une page à l’autre (header, footer, barre latérale…). On les appelle « template parts » ou « partials ».

Pour insérer ces templates au sein de notre layout ou de nos pages, on exploite la mécanique d’inclusion de Twig. Cela consiste concrètement à factoriser une portion de code dans un fichier spécifique et à l’inclure avec la fonction Twig include().

Il y a 2 cas de figures pour lesquels il est judicieux d’inclure un template : bien organiser ses templates et éviter de dupliquer son code.

Organiser ses templates

Si vous ne voulez pas qu’une portion de code vienne trop complexifier la structure de votre page, c’est une manière de l’isoler proprement (header, footer, sidebar…).

Au lieu de coder l’en-tête et le pied de page directement dans le layout 📄 base.html.twig, il serait plus judicieux de les créer dans des fichiers distincts 📄 _header.html.twig et 📄 _footer.html.twig, puis de les inclure dans le layout.

_header.html.twig
copié !
<header>...</header>
_footer.html.twig
copié !
<footer>...</footer>
base.html.twig
copié !
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>{% block title %}{% endblock %} - Nom site</title>
	</head>
	<body>
		{{ include('_header.html.twig') }}
		{% block body %}{% endblock %}
		{{ include('_footer.html.twig') }}
	</body>
</html>

Éviter la duplication de code

Si un template est appelé plusieurs fois / sur plusieurs pages, il est alors judicieux de le factoriser. #DRY

Ce scénario se produit par exemple :

  • Pour une bannière d’inscription à une newsletter que l’on retrouverait sur plusieurs pages d’un site web
  • Pour les formulaires d’ajout et d’édition des articles d’un blog (ces formulaires sont structurellement identiques, à la seule différence qu’à l’édition ils seront préremplis).
_newsletter.html.twig
copié !
<form>...</form>
page1.html.twig
copié !
{% extends 'base.html.twig' %}

{% block title %}Page 1{% endblock %}

{% block body %}
	<section>...</section>
	<section>
		{{ include('app/_newsletter.html.twig') }}
	</section>
	<section>...</section>
{% endblock %}
page2.html.twig
copié !
{% extends 'base.html.twig' %}

{% block title %}Page 2{% endblock %}

{% block body %}
	<section>...</section>
	<section>
		{{ include('app/_newsletter.html.twig') }}
	</section>
	<section>...</section>
{% endblock %}

Si, en toute logique, la portion de code incluse va bénéficier de l’ensemble des variables de notre page, notez qu’il est également possible de faire passer des variables additionnelles à un fichier que l’on va inclure. Cela se fait avec le mot-clé with, de la manière suivante :

copié !
{{ include '_partial.html.twig' with { 'ma_variable': 'sa_valeur' } }}
Inclure une méthode de contrôleur

Si la fonction include() est généralement utilisée pour inclure des templates, notez qu’il est aussi possible d’inclure des méthodes de contrôleur toutes entières avec la fonction Twig render().

Parfois, nous allons inclure des templates mais ne posséderons pas forcément toutes les variables qu’ils utilisent.

Par exemple, imaginons que sur toutes les pages d’un blog nous souhaitons afficher dans une sidebar les 5 derniers articles publiés.

Cette sidebar sera en toute logique créée dans un « partial » 📄 _last_articles.html.twig.

_last_articles.html.twig
copié !
{% for article in lastArticles %}
	<h2><a href="#">{{ article.title }}</a></h2>
{% endfor %}

Méthode 1 (❌ mauvaise)

  1. J’inclus le template 📄 _last_articles.html.twig dans toutes mes pages
  2. Je suis contraint de récupérer les 5 derniers articles publiés dans chacune de mes méthodes de contrôleur afin de les transmettre aux pages.

Méthode 2 (✅ bonne)

J’inclus une méthode de contrôleur lastArticles(5) chargée de récupérer les 5 derniers articles du blog et de les afficher dans le template 📄 _last_articles.html.twig.

Notre méthode de contrôleur ressemblera à cela :

BlogController.php
copié !
public function lastArticles(int $nb): Response {
	// Plus tard, on récupérera en base de données, dans la variable $lastArticles, les $nb derniers articles
	return $this->render('blog/_last_articles.html.twig', array(
		'lastArticles' => $lastArticles
	));
}

Pour inclure cette méthode de contrôleur dans un template, il suffit de remplacer la fonction include() par render(controller()) :

copié !
{{ render(controller("App\\Controller\\AppController::lastArticles")) }}

Le paramètre de render(controller()) est une chaîne de caractères de la forme : namespace\\contrôleur::méthode, en remplaçant bien le simple antislash du namespace de votre contrôleur, par un double antislash.

Héritage VS inclusion

Au début, il peut arriver d’hésiter lorsqu’il s’agit de choisir entre héritage et inclusion. Voici donc quelques tips pour vous aider dans ce choix :

HéritageInclusion
Si le template n’est inclus que dans un seul template (parent).Si le template doit être inclus dans plusieurs templates.
Si le template inclus dépend de l’autre (parent).Si le template ne dépend d’aucun autre.
Si le template inclus doit modifier plusieurs zones de l’autre template (parent).

Liens entre pages

Les liens Twig vont nous permettre de naviguer sur notre site en appelant les routes créées dans notre système de routage.

Ceci fonctionnera de manière dynamique et nous permettra d’éviter d’écrire nous-mêmes les URL « en dur » dans l’attribut href.

On appelle une route via la fonction Twig path() en y précisant son nom (voilà pourquoi il était important de donner un nom à nos routes 😉) et ses paramètres, si elle en a.

copié !
<a href="{{ path('app_home') }}">Accueil</a>
copié !
<a href="{{ path('article_list', { slug: 'dev-web' }) }}">Développement web</a>

Algorithmie

Dans Twig, il est possible de faire appel aux structures algorithmiques classiques (variables, boucles, conditions…) afin de pousser plus loin la dynamique de nos pages.

Variables

Pour dynamiser nos pages web, on va généralement faire appel à des variables.

Pour afficher une variable, on écrit son nom entre les délimiteurs {{}}.

Notre bon vieux <?php echo $ma_variable; ?> ou <?= $ma_variable; ?> sera donc remplacé par {{ ma_variable }}.

Quand je parle de variable ici, je fais allusion à nos structures de stockage qui vont nous permettre de stocker des informations de manière globale : cela signifie des variables simples, mais aussi des tableaux ou encore des objets.

La syntaxe varie très légèrement en fonction :

Variable{{ ma_variable }}
Tableau{{ mon_tableau[0] }}, {{ mon_tableau['cle'] }} ou {{ mon_tableau.cle }}
Objet{{ mon_objet.propriete }}

Mais ces variables ne proviennent pas toujours du même endroit, on peut :

Les faire passer depuis nos contrôleurs

Pour récupérer une variable provenant du contrôleur, on l’appelle par le même nom que celui qu’on lui a donné au moment de la transmettre au template dans notre fonction render() :

copié !
#[Route('/blog/{slug}', name: 'article_show')]
public function showArticle(string $slug): Response {
	// Ici, on récupèrera dans la variable $article, toutes les informations de l'article en question
	return $this->render('blog/show.html.twig', ['article' => $article]);
}
  • 'article' : nom de la variable transmise à TWIG
  • $article : variable PHP contenant l’information

La variable TWIG 'article' prend pour valeur le contenu de la variable PHP $article.

Maintenant côté TWIG on peut exploiter notre objet article de la manière suivante :

copié !
{{ article.title }}
Les déclarer dans nos templates

Il est aussi possible de déclarer des variables dans nos templates TWIG. Pour cela on utilise le mot-clé set.

copié !
{% set a = 'Salut' %}
Aller la chercher dans notre application Symfony (variables globales)

Parfois, nous allons avoir besoin d’informations particulières sur la requête HTTP, une session, l’utilisateur authentifié, l’environnement courant…

Tout un tas d’informations utiles que Symfony nous offre dans une variable globale Twig appelée app.

VariableDescription
{{ app.request }}Les informations de la requête HTTP
{{ app.session }}Le système des sessions
{{ app.environment }}L’environnement courant : dev, prod (ou d’autres que vous auriez définis)
{{ app.debug }}Un booléen renvoyant si le mode debug est actif ou non
{{ app.user }}Si un utilisateur est authentifié, on peut accéder à ses informations comme ceci
{{ app.flashes }}Pour afficher des messages flashs stockés en session

Il est également possible de déclarer nos propres variables globales Twig pour y stocker des informations personnelles, comme un code de tracking de Google Analytics, une clé API, des informations de contact ou encore les crédits du site, etc.

Cela se passe dans la section globals du fichier 📄 config/packages/twig.yaml :

config/packages/twig.yaml
copié !
twig:
	globals:
		ga_tracking_code: "UA-874279433-1"
		developer: "Fabien Potencier"
		contact: "[email protected]"

Ensuite, il ne reste plus qu’à l’appeler côté template .html.twig.

copié !
{{ ga_tracking_code }}

Concaténation

Pour concaténer en Twig, on sépare la variable de la chaîne de caractères avec une ~ (tilde).

copié !
{{ var_1 ~ " concaténé avec " ~ var_2 }}

Fonctions

Twig possède de nombreuses fonctions dont certaines ont même été ajoutées pour le framework Symfony.

Nous en avons jusqu’à présent utilisées certaines comme include(), path(), parent() ou encore render().

Filtres

Un filtre est une fonction qui permet de manipuler et de formater des données dans des templates Twig.

Ces données peuvent être des chaînes de caractère définies « en dur », des variables TWIG ou d’autres fonctions (avec valeur de retour). Ces filtres se notent après le caractère |.

Filtres sans paramètre

Les filtres sans paramètres permettent de réaliser des traitements basiques.

copié !
{{ ma_variable|upper }}
{{ ma_variable|lower }}
{{ ma_variable|length }}
  • upper : mise en majuscule d’une chaîne de caractère
  • lower : mise en minuscule d’une chaîne de caractère
  • length : retourne la longueur

Filtres avec paramètre

Certains filtres peuvent êtres utilisés avec des arguments, ce qui les rend plus complets.

copié !
{{ article.createdAt|date('d/m/Y') }}
{{ title|replace({'o': '0'}) }}
  • Le filtre |date permet de formater une date (nécessairement un objet de type Datetime). Le format de la date est donc un paramètre que l’on va renseigner lors de l’appel de ce filtre. Ici, la date est formatée au format français : dd/mm/YYYY.
  • Le filtre |replace permet de remplacer des chaînes de caractères spécifiques par d’autres. Ici, tous les o contenus dans la variabe title seront remplacés par des 0.

Filtres combinés

Il est possible d’appliquer plusieurs filtres en les plaçant les uns à la suite des autres. Ils s’exécuteront dans l’ordre d’écriture.

copié !
{{ "Framework Symfony"|replace({'o': '0', 'e': '3', 'a': '@'})|lower }}

La chaîne de caractère « Framework Symfony » deviendra :

  1. D’abord « Fr@m3w0rk Symf0ny »
  2. Puis « fr@m3w0rk symf0ny »
Point sécurité

Fini le temps où on craignait les failles XSS en PHP et où on était obligé de faire appel à la fonction htmlspecialchars() à l’affichage de variables pour nous protéger d’éventuels scripts nocifs. Twig applique automatiquement à vos variables un filtre par défaut qui va échapper les caractères spéciaux.

Si une variable contient la valeur « <b>je suis en gras</b> », par défaut, Twig génèrera le texte « &lt;b&gt;je suis en gras&lt;/b&gt; », qui affichera les caractères sans les interpréter.

Mais il peut arriver que cet échappement nous embête dans certains cas.

Imaginons que nous ayons un éditeur visuel de type WYSIWYG lors de la rédaction du contenu d’articles de blog. On va donc mettre en forme notre article via l’éditeur, et lors de son enregistrement en base de données, des balises HTML contenant la mise en forme de notre article seront stockées.

Si on affiche le champ qui contient le contenu, nos balises HTML seront échappées par Twig, et s’afficheront donc à l’écran comme du texte… ce qui n’est pas ce qu’on espérait !

Pour éviter ce scénario, Twig va mettre à notre disposition le filtre |raw qui va désactiver localement l’échappement des caractères spéciaux (« raw » se traduit littérallement par « brut »), et interpréter le code qu’elle contient :

copié !
{{ article.content|raw }}

Nous n’avons utilisé ici que quelques filtres à des fins pédagogiques. La liste complète des filtres Twig est accessible sur la documentation officielle.

Conditions

Le rôle d’une condition, aussi appelée « structure de contrôle », est d’afficher du contenu, sous certaines conditions.

La structure de contrôle utilise les mots-clés if, elseif, else.

copié !
{% if user['role'] == "Admin" %}
	<p>Bienvenue admin !</p>
{% else %}
	<p>Oups, cet endroit n'est pas pour toi !</p>
{% endif %}
  • On déclare donc un bloc conditionnel avec {% if condition %}
  • On ferme un bloc conditionnel avec {% endif %}
  • Entre les deux il est tout à fait possible d’ajouter des {% elseif %} et {% else %}

Boucles

Le rôle d’une boucle, aussi appelée « structure itérative », est la répétition automatisée d’une portion de code. La structure itérative utilise le mot-clé « for ».

La boucle for offre de nombreuses variations la rendant très utile dans de nombreux cas. Vous retrouverez en détails l’ensemble de ces applications sur la documentation officielle.

Dans notre cas nous nous attarderons uniquement sur sa déclinaison foreach (notée for ... in ...), afin de parcourir des tableaux d’objets, car c’est sous cette forme que nous exploitons généralement nos données dans une application moderne.

copié !
{% for article in articles %}
	<h2><a href="{{ path('article_show', { slug: article.slug }) }}">{{ article.title }}</a></h2>
{% endfor %}
  • On déclare un bloc itératif avec {% for ... %}
  • On ferme un bloc itératif avec {% endfor %}.

Twig a pensé à tout en nous permettant de faire appel à {% else %} qui va s’exécuter si le tableau articles est vide. Et inclure un else dans un for, c’est plutôt fort (ou for…).

copié !
{# ✅ Bien ! #}
{% for article in articles %}
	<h2><a href="{{ path('article_show', { slug: article.slug }) }}">{{ article.title }}</a></h2>
{% else %}
	<p>Désolé, il n'y a pas encore d'articles...</p>
{% endfor %}

{# 😔 Maladroit #}
{% if articles|length > 0 %}
	{% for article in articles %}
		<h2><a href="{{ path('article_show', { slug: article.slug }) }}">{{ article.title }}</a></h2>
	{% endfor %}
{% else %}
	<p>Désolé, il n'y a pas encore d'articles...</p>
{% endif %}

Et comme Twig n’a pas pour habitude de faire dans la demi-mesure, il met également à notre disposition une variable loop au sein de la boucle, qui contient les propriétés suivantess :

PropriétéDescription
{{ loop.index }}Numéro de l’itération courante (commence à 1)
{{ loop.index0 }}Numéro de l’itération courante (commence à 0)
{{ loop.revindex }}Nombre d’itérations restantes avant la fin de la boucle (finit par 1)
{{ loop.revindex0 }}Nombre d’itérations restantes avant la fin de la boucle (finit par 0)
{{ loop.first }}Booléen renvoyant true s’il s’agit de la première itération
{{ loop.last }}Booléen renvoyant true s’il s’agit de la dernière itération
{{ loop.length }}Nombre total d’itérations de la boucle

Lier du CSS, JS ou une image

Symfony met à disposition la fonction Twig asset() qui permet de générer des URL vers des ressources internes, placées dans notre répertoire 📁 public.

copié !
{# Un logo situé dans "public/img/logo.png" #}
<img src="{{ asset('img/logo.png') }}" alt="Logo - Nom marque">

{# Un fichier CSS situé dans "public/css/app.css" #}
<link href="{{ asset('css/app.css') }}" rel="stylesheet">

{# Un fichier JS situé dans "public/js/app.js" #}
<script src="{{ asset('js/app.js') }}"></script>