Skip to Content
SymfonyPartie 7 — Twig, AssetMapper & back-office7.2 — Héritage, includes & composants

Chapitre 7.2 — Héritage, includes & composants Twig

Partie 7 : Twig & back-office — Chapitre 2/5 Version de référence : Twig 3 / Symfony UX Twig Component / Symfony 7.4.

⏱️ TL;DR

  • Héritage : un template base.html.twig définit des blocs ({% block body %}) ; les pages font {% extends 'base.html.twig' %} et remplissent ces blocs. {{ parent() }} réutilise le contenu parent.
  • {% include %} insère un fragment ; {% embed %} l’insère en surchargeant ses blocs.
  • Les composants Twig (Symfony UX) sont l’équivalent serveur des composants React : une classe (props, logique) + un template, appelés en HTML <twig:Alert type="success">…</twig:Alert>.
  • Il existe aussi des composants anonymes (juste un template dans templates/components/), sans classe.
  • Pour un dev Next.js : l’héritage Twig ≈ un layout Next (layout.tsx + children), et les composants Twig ≈ vos composants React (props, réutilisables) — mais rendus côté serveur.

🎯 Objectifs

  • Structurer les pages avec l’héritage de templates et les blocs.
  • Réutiliser des fragments avec include/embed et des macros.
  • Créer des composants Twig avec props et logique.
  • Choisir entre include, macro et composant.

1. L’héritage : un squelette commun

Toute application a un gabarit commun (en-tête, navigation, pied de page). On le définit une fois dans base.html.twig, avec des blocs que les pages rempliront.

{# templates/base.html.twig #} <!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <title>{% block title %}BookLab{% endblock %}</title> {% block stylesheets %}{% endblock %} </head> <body> <header> <a href="{{ path('home') }}">BookLab</a> {% block nav %}{% endblock %} </header> <main> {% for label, messages in app.flashes %} {% for message in messages %} <div class="flash flash-{{ label }}">{{ message }}</div> {% endfor %} {% endfor %} {% block body %}{% endblock %} </main> {% block javascripts %}{% endblock %} </body> </html>

Une page étend ce gabarit et remplit les blocs :

{# templates/booking/list.html.twig #} {% extends 'base.html.twig' %} {% block title %}Réservations — {{ parent() }}{% endblock %} {% block body %} <h1>Réservations</h1> <table> {% for booking in bookings %} <tr><td>{{ booking.customerEmail }}</td><td>{{ booking.status.label }}</td></tr> {% endfor %} </table> {% endblock %}

{{ parent() }} insère le contenu du bloc parent (ici, « BookLab » s’ajoute au titre). Une page ne remplit que les blocs qui l’intéressent ; les autres gardent leur contenu par défaut.

💡 Pour un dev Next.js : base.html.twig + {% block body %} ≈ un layout.tsx avec {children}. {% extends %} correspond à « cette page s’insère dans ce layout ». La différence : Twig permet plusieurs blocs nommés (title, nav, body, scripts…), pas seulement children.

2. Includes et embeds

include insère un fragment, éventuellement avec des variables :

{% include 'partials/_booking_row.html.twig' with { booking: booking } %} {# le fragment reçoit 'booking' ; 'only' isole le contexte : #} {% include 'partials/_badge.html.twig' with { status: booking.status } only %}

embed est un include surchargeable : on insère un fragment et on redéfinit certains de ses blocs — utile pour des « cartes » ou « modales » réutilisables dont on personnalise le contenu.

{% embed 'partials/_card.html.twig' %} {% block card_title %}Réservation n°{{ booking.id }}{% endblock %} {% block card_body %}{{ booking.customerEmail }}{% endblock %} {% endembed %}

Convention : on préfixe les fragments d’un underscore (_booking_row.html.twig) pour signaler « ce n’est pas une page ».

3. Macros : des « fonctions » de template

Une macro est une fonction réutilisable qui produit du HTML — pratique pour de petits éléments répétés (un badge, un bouton) sans créer un fichier par fragment.

{# templates/macros/ui.html.twig #} {% macro statusBadge(status) %} <span class="badge badge-{{ status.value }}">{{ status.label }}</span> {% endmacro %}
{% import 'macros/ui.html.twig' as ui %} {{ ui.statusBadge(booking.status) }}

Les macros sont sans état et purement présentationnelles. Dès qu’il faut de la logique (calculs, dépendances), on passe aux composants Twig.

4. Les composants Twig : le React côté serveur

C’est la fonctionnalité moderne à connaître. Un composant Twig (bundle symfony/ux-twig-component) associe une classe PHP (props typées, logique) à un template, et s’appelle en HTML comme une balise.

composer require symfony/ux-twig-component
// src/Twig/Components/StatusBadge.php use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; use App\Enum\BookingStatus; #[AsTwigComponent] final class StatusBadge { public BookingStatus $status; // prop // logique : une propriété calculée exposée au template public function cssClass(): string { return 'badge badge-' . $this->status->value; } }
{# templates/components/StatusBadge.html.twig #} <span class="{{ this.cssClass }}">{{ status.label }}</span>

On l’utilise dans n’importe quel template avec la syntaxe balise :

<twig:StatusBadge :status="booking.status" />

:status="..." passe une expression (l’enum) ; status="pending" passerait une chaîne. Dans le template du composant, this est l’instance de la classe (this.cssClass), et les props sont accessibles directement (status).

💡 Pour un dev Next.js : c’est littéralement un composant React, côté serveur : class StatusBadge { status } ≈ les props, cssClass() ≈ une fonction dérivée, <twig:StatusBadge :status="..." /><StatusBadge status={...} />. Vous retrouvez la composition par composants — sans hydratation JS (ça reste du HTML). La Partie 8 (LiveComponents) ajoutera la réactivité à ces composants.

Il existe aussi des composants anonymes : un simple template dans templates/components/ (sans classe), pour des composants purement présentationnels — encore plus léger qu’une macro, avec la syntaxe balise.

5. Include vs macro vs composant : lequel ?

BesoinOutil
Insérer un bout de page, contexte simpleinclude
Insérer + surcharger des blocsembed
Petit élément présentationnel répétémacro ou composant anonyme
Élément réutilisable avec props typées + logiquecomposant Twig (classe)
Élément réactif (mise à jour sans recharger)LiveComponent (Partie 8)

✏️ Exercices

Exercice 1. Créez un base.html.twig minimal avec des blocs title et body, puis une page provider/show.html.twig qui l’étend et affiche le nom du prestataire.

✅ Solution

{# base.html.twig #} <!DOCTYPE html> <html lang="fr"><head><title>{% block title %}BookLab{% endblock %}</title></head> <body>{% block body %}{% endblock %}</body></html>
{# provider/show.html.twig #} {% extends 'base.html.twig' %} {% block title %}{{ provider.name }} — {{ parent() }}{% endblock %} {% block body %}<h1>{{ provider.name }}</h1>{% endblock %}

Exercice 2. Transformez cette macro statusBadge en composant Twig avec une classe, exposant une méthode cssClass().

✅ Solution

Créer src/Twig/Components/StatusBadge.php avec #[AsTwigComponent], une prop public BookingStatus $status; et une méthode cssClass(): string. Créer templates/components/StatusBadge.html.twig avec <span class="{{ this.cssClass }}">{{ status.label }}</span>. L’utiliser via <twig:StatusBadge :status="booking.status" />. (Voir le §4 pour le code complet.) Avantage sur la macro : props typées et logique dans une classe testable.

Exercice 3. Quand choisir un include plutôt qu’un composant Twig ?

✅ Solution

include convient pour insérer un fragment de page simple, sans logique ni props typées (ex. une ligne de tableau, un en-tête réutilisé), en lui passant quelques variables. Le composant Twig (classe) s’impose dès qu’il faut des props typées, de la logique (méthodes dérivées), une réutilisation cadrée et testable — bref, un vrai « composant » plutôt qu’un simple copier-coller de balisage.

🧠 Quiz de révision

1. Comment une page réutilise-t-elle un gabarit commun ?

Avec {% extends 'base.html.twig' %} puis en remplissant les blocs ({% block body %}…{% endblock %}). {{ parent() }} réutilise le contenu du bloc parent.

2. Différence entre include et embed ?

include insère un fragment tel quel ; embed l’insère en permettant de surcharger ses blocs (fragment personnalisable).

3. Qu’est-ce qu’un composant Twig ?

Une classe PHP (props typées + logique) associée à un template, appelée en HTML <twig:Nom :prop="..." /> — l’équivalent serveur d’un composant React.

4. Dans le template d’un composant, à quoi correspond this ?

À l’instance de la classe du composant : on accède à ses méthodes/propriétés (this.cssClass), et aux props directement (status).

5. Macro ou composant pour un élément avec logique et props typées ?

Un composant Twig (classe) : les macros sont sans état et présentationnelles ; les composants portent props typées et logique.


Prochain chapitre : 7.3 — AssetMapper — servir CSS/JS modernes sans bundler.