Dans Twig, l’inclusion de fragments de template sert à découper une interface sans répéter la même structure partout. Le sujet autour de twig include revient vite à une question concrète : comment réutiliser un bloc proprement, sans perdre le contexte, la sécurité ni la lisibilité. C’est particulièrement utile dans un backend PHP, pour les vues partielles, les emails transactionnels et les composants d’administration.
Ce qu’il faut garder en tête avant de l’utiliser
- La fonction
includerend un template et réinjecte son contenu dans le template courant. - La documentation Twig recommande aujourd’hui la fonction plutôt que le tag, car elle se compose mieux dans le code.
- Le contexte est partagé par défaut, mais on peut le restreindre avec
with_context: falseouonly. -
ignore missingévite de casser le rendu quand un fragment optionnel n’existe pas. - Pour des blocs internes à surcharger,
embedest souvent plus adapté qu’une simple inclusion. - En backend, c’est un outil de découpage, pas un substitut à l’héritage global de layout.
Comprendre ce que fait vraiment l’inclusion dans Twig
La logique de base est simple : un template en appelle un autre, puis récupère le HTML rendu dans le flux de sortie courant. La fonction include renvoie donc du contenu déjà compilé par Twig, ce qui en fait un outil de composition très pratique pour les fragments réutilisables.
Je distingue toujours deux cas. Le premier, c’est le fragment statique ou semi-statique, comme une alerte, un bloc de navigation ou un pied de page d’email. Le second, c’est le fragment qui dépend de données passées depuis le backend, par exemple une carte de statistiques ou un bloc d’état dans une interface d’administration.
La documentation Twig conseille la fonction include plutôt que le tag, parce qu’elle s’intègre mieux avec les filtres, les variables intermédiaires et les arguments nommés. En pratique, cela me donne un code plus lisible quand je dois stocker le rendu dans une variable, le filtrer ou le réutiliser dans une autre expression.
{{ include('partials/_alert.html.twig') }}
{% set content = include('emails/_signature.html.twig') %}
{{ include(ajax ? 'partials/_ajax_card.html.twig' : 'partials/_card.html.twig') }}
Le point important à retenir, c’est que Twig ne fait pas ici de l’héritage de page, mais de la composition de fragments. Cette nuance devient décisive dès qu’on commence à contrôler le contexte des variables, ce que je détaille juste après.
Maîtriser le contexte et les variantes d’appel
Par défaut, le template inclus voit les variables du contexte courant. C’est pratique, mais ce n’est pas toujours souhaitable. Quand je veux un composant plus prévisible, je lui passe explicitement ce dont il a besoin, et rien de plus.
{{ include('partials/_card.html.twig', { title: 'Statut', value: stats.active }) }}
{{ include('partials/_card.html.twig', { title: 'Statut', value: stats.active }, with_context: false) }}
{{ include('partials/_badge.html.twig', { label: 'Beta' }, with_context: false) }}
Le premier appel laisse passer le contexte actuel, le deuxième le coupe complètement. C’est une discipline utile dans les projets backend un peu volumineux, parce qu’elle limite les dépendances invisibles entre templates. Si un fragment casse dès qu’une variable globale disparaît, c’est souvent le signe qu’il dépend trop du contexte ambiant.
Deux autres options méritent une vraie attention. only permet de n’exposer qu’un sous-ensemble de variables, ce qui protège des collisions de noms et clarifie le contrat du fragment. ignore missing, lui, permet de rendre le template optionnel sans lever d’exception si le fichier n’existe pas.
{{ include('partials/_promo_banner.html.twig', { title: 'Offre' }, with_context: false) }}
{{ include('partials/_beta_banner.html.twig', ignore_missing: true) }}
{{ include('partials/_user_menu.html.twig', { user: user }, with_context: false) }}
Je garde aussi en tête que le nom du template peut être une expression Twig valide. Cela ouvre la porte à des inclusions conditionnelles ou à des listes de secours, très utiles quand on gère plusieurs variantes de contenu selon la langue, le canal ou le niveau de personnalisation. Cette souplesse devient particulièrement intéressante côté backend dès qu’une interface, un email ou un bloc de page doit s’adapter à plusieurs scénarios.
L’utiliser proprement dans un backend orienté API
Dans un backend, je réserve l’inclusion aux couches de présentation qui ont besoin de répétition maîtrisée. Je ne l’emploie pas pour construire une API JSON pure, mais pour tout ce qui reste du côté du rendu HTML : back-office, emails transactionnels, pages d’erreur, vues de support ou documentation générée côté serveur.
Les cas d’usage les plus solides sont assez concrets :
- les emails transactionnels, où l’on réutilise un en-tête, un pied de page et parfois un bloc de CTA identique d’un message à l’autre ;
- les interfaces d’administration, où des cartes, alertes et tableaux partagent souvent la même structure ;
- les pages d’erreur ou de maintenance, qui ont besoin d’un rendu cohérent sans dupliquer le squelette complet ;
- les fragments conditionnels, quand une partie du contenu dépend d’une fonctionnalité activée ou d’un profil utilisateur.
Par exemple, pour un email d’expédition de commande, je peux inclure un bloc commun puis basculer vers une variante locale si elle existe. Cette approche évite les if/else dispersés dans tout le template et réduit les risques d’incohérence entre langues ou canaux.
{{ include(['emails/order_shipped.fr.html.twig', 'emails/order_shipped.html.twig']) }}
{{ include('emails/_header.html.twig', { brand: brand }, with_context: false) }}
{{ include('emails/_footer.html.twig', { support_email: support_email }, with_context: false) }}
Ce type de structure fonctionne bien quand le backend orchestre plusieurs contenus, mais qu’il faut garder une présentation uniforme. Le vrai gain n’est pas seulement la réutilisation du code, c’est la stabilité du rendu quand l’application grandit.
À l’inverse, si le fragment doit recevoir beaucoup de données implicites depuis le contexte général, je ralentis volontairement. C’est souvent le symptôme d’un template trop couplé au contrôleur ou à des variables globales trop nombreuses, et ce n’est pas un bon signe dans une architecture backend sérieuse.
Choisir entre include, embed et extends sans se tromper
Quand l’interface commence à se fragmenter, il faut choisir le bon niveau de composition. C’est là que beaucoup d’équipes se trompent : elles utilisent include là où il faudrait un véritable squelette de page, ou bien elles surchargent un fragment alors qu’un simple rendu suffit.
| Mécanisme | Ce qu’il fait | Quand je le choisis | Limite principale |
|---|---|---|---|
include |
Rend un fragment dans le template courant | Composants réutilisables, alertes, emails, blocs simples | Ne permet pas de redéfinir des blocs internes |
embed |
Inclut un template et autorise la surcharge de blocs internes | Micro-layouts, fragments structurés, variantes de carte | Plus verbeux et plus lourd à lire |
extends |
Hérite d’un squelette global | Pages complètes, layouts de base, structure de site | Trop large pour un simple composant |
include() |
Fonction de rendu, plus composable dans le code | Code moderne, variables intermédiaires, filtres, expressions | Il faut garder une discipline sur le contexte |
Si je devais résumer ma règle personnelle, je dirais ceci : include pour les morceaux, embed pour les morceaux avec structure interne, extends pour le squelette. Cette hiérarchie évite de transformer un projet Twig en empilement de fichiers difficiles à relire.
Dans la pratique, la fonction d’inclusion est souvent le choix par défaut, parce qu’elle est simple et lisible. Mais dès qu’un fragment commence à avoir ses propres zones remplaçables, je passe à embed sans hésiter. C’est cette frontière qui fait la différence entre un template propre et un faux composant qui finit par ressembler à une mini page autonome.
Les erreurs qui coûtent du temps en production
Les problèmes les plus fréquents ne viennent pas de Twig lui-même, mais de la manière dont on utilise l’inclusion. Quand je relis un projet, ce sont presque toujours les mêmes pièges qui reviennent.
- Confondre inclusion et héritage. Un fragment inclus ne remplace pas une architecture de layout.
- Laisser passer trop de variables implicites. Le composant devient alors difficile à tester et à réutiliser.
- Réutiliser un bloc HTML dans un contexte non HTML. Twig renvoie du contenu marqué comme sûr pour le même contexte, pas pour tous les contextes possibles.
- Oublier le cas où le fichier manque. Sans stratégie claire, une variante locale peut faire tomber le rendu.
- Inclure du contenu issu d’un utilisateur sans sandboxing. C’est un vrai sujet de sécurité si le template n’est pas totalement maîtrisé.
Sur ce dernier point, je suis volontairement strict. Si un template peut venir d’une source non fiable, je ne le traite jamais comme un simple fragment inoffensif. Twig propose un mode sandbox pour ce genre de cas, et c’est le bon réflexe dès qu’on sort du périmètre des templates contrôlés par l’équipe.
Je vois aussi souvent une autre dérive : multiplier les fichiers pour gagner en propreté apparente, puis finir avec une chaîne d’inclusions très difficile à suivre. Le remède n’est pas de tout regrouper à nouveau, mais de stabiliser les frontières entre les fragments, de nommer clairement ce qu’ils attendent, et de ne pas les faire dépendre d’un contexte trop large.
La règle que j’applique avant d’ajouter une inclusion de plus
Avant de créer un nouveau fragment, je me pose trois questions simples : est-ce que ce bloc a une vraie réutilisation, est-ce qu’il peut vivre avec un contrat de données clair, et est-ce que son absence peut être gérée sans casser l’écran ? Si la réponse est oui, l’inclusion a du sens.
Dans un backend bien structuré, c’est souvent cette discipline qui fait la différence. L’objectif n’est pas de découper au maximum, mais de découper juste assez pour que chaque pièce reste lisible, testable et facile à remplacer. C’est aussi pour cela que je garde une préférence nette pour la fonction include dans le code moderne, tout en réservant embed aux fragments qui ont une vraie logique interne.
En pratique, la meilleure approche est rarement la plus spectaculaire : un fragment clair, un contexte explicite, une stratégie de secours quand un template est optionnel, et un héritage de page limité aux vrais layouts. Avec cette base, Twig reste rapide à maintenir et beaucoup moins fragile quand le projet grandit.