Création d'un composant
Créer un composant "Symfony UX" pour Quiz interactif

Notions théoriques
Qu'est-ce qu'un composant Twig dans Symfony UX ?
Symfony UX propose 2 types de composants réutilisables côté template : les Twig Components et les Live Components.
Dans ce cours, on travaille avec les Twig Components, qui constituent la base de tout composant Symfony UX personnalisé.
Un Twig Component est une classe PHP associée à un template Twig. Ensemble, ils forment une unité autonome et réutilisable que l'on peut appeler depuis n'importe quel template de l'application, comme une balise HTML personnalisée.
L'idée est simple : plutôt que de copier-coller du HTML et de la logique PHP dans plusieurs templates, on encapsule tout dans un composant.
On l'appelle ensuite avec une syntaxe concise :
<twig:Quiz :questions="questions" />
Cette ligne suffit à afficher un quiz complet, avec toute sa logique et son rendu visuel.
Les 2 packages nécessaires
Pour créer des composants Twig dans Symfony UX, il faut installer deux packages :
symfony/ux-twig-component: fournit la classe de baseAbstractComponentet le système de rendu des composants Twigsymfony/ux-stimulus-bundle: fournit Stimulus pour gérer les interactions JavaScript (déjà installé avec--webapp)
composer require symfony/ux-twig-component
Si le projet a été créé avec symfony new mon-projet --webapp, le bundle Stimulus est déjà installé. Seul ux-twig-component peut manquer.
La structure d'un Twig Component
Un composant Twig se compose de deux fichiers :
1. La classe PHP — placée dans src/Twig/Components/
// src/Twig/Components/Alert.php
namespace App\Twig\Components;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
class Alert
{
public string $type = 'info';
public string $message = '';
}
2. Le template Twig — placé dans templates/components/
{# templates/components/Alert.html.twig #}
<div class="alert alert-{{ type }}">
{{ message }}
</div>
La convention de nommage est stricte : si la classe s'appelle Alert, le template doit s'appeler Alert.html.twig dans templates/components/. Symfony UX fait le lien automatiquement grâce à l'attribut #[AsTwigComponent].
L'attribut PHP #[AsTwigComponent]
L'attribut #[AsTwigComponent] (introduit en PHP 8.0) est ce qui transforme une classe PHP ordinaire en composant Twig. Il indique à Symfony UX que cette classe est un composant et lui permet de :
- Détecter automatiquement le template associé
- Exposer les propriétés publiques de la classe comme variables Twig
- Permettre l'appel du composant depuis un template avec
<twig:NomDuComposant />
On peut aussi personnaliser le nom du composant :
#[AsTwigComponent('mon-quiz')]
class Quiz { ... }
Ce qui permettrait de l'appeler avec <twig:mon-quiz />.
Les propriétés publiques : le pont PHP/Twig
Les propriétés publiques de la classe PHP sont automatiquement accessibles dans le template Twig du composant. C'est le mécanisme central de la communication entre la logique PHP et le rendu HTML.
#[AsTwigComponent]
class Quiz
{
public string $titre = 'Quiz sans titre';
public array $questions = [];
public bool $afficherScore = true;
}
Dans le template, on accède directement à titre, questions et afficherScore sans déclaration supplémentaire.
Seules les propriétés publiques sont exposées au template. Les propriétés protected et private restent inaccessibles depuis Twig — elles peuvent servir pour la logique interne de la classe.
Passer des données au composant
Lorsqu'on appelle un composant depuis un template parent, on lui passe des données via des attributs HTML-like :
{# Appel avec des valeurs statiques #}
<twig:Alert type="danger" message="Une erreur est survenue" />
{# Appel avec des variables Twig (préfixe : pour évaluer l'expression) #}
<twig:Alert :type="alertType" :message="alertMessage" />
Le préfixe : avant un attribut indique à Symfony UX d'évaluer l'expression Twig passée en valeur, plutôt que de la traiter comme une chaîne de caractères.
Sans le préfixe :, la valeur est toujours interprétée comme une chaîne de caractères. type="danger" passe la chaîne "danger", tandis que :type="monType" passe la valeur de la variable Twig monType.
Les méthodes dans un composant
Une classe de composant peut contenir des méthodes publiques, accessibles depuis le template :
#[AsTwigComponent]
class Quiz
{
public array $questions = [];
public function getNombreQuestions(): int
{
return count($this->questions);
}
}
Dans le template :
<p>Ce quiz contient {{ this.getNombreQuestions() }} question(s).</p>
Dans le template d'un composant, this désigne l'instance de la classe PHP du composant. On accède aux méthodes avec this.nomDeLaMethode() et aux propriétés avec nomDeLaPropriete.
Ajouter de l'interactivité avec Stimulus
Un Twig Component est statique : il génère du HTML côté serveur, sans interaction côté client. Pour ajouter de l'interactivité (comme révéler la bonne réponse au clic), on utilise Stimulus en ajoutant des attributs data-controller et data-action dans le template du composant.
<div data-controller="quiz">
<button data-action="click->quiz#verifier">Vérifier ma réponse</button>
<p data-quiz-target="resultat"></p>
</div>
Et dans un contrôleur Stimulus créé dans assets/controllers/quiz_controller.js :
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['resultat'];
verifier() {
this.resultatTarget.textContent = 'Bonne réponse !';
}
}
Cette combinaison Twig Component (logique PHP + rendu HTML) + Stimulus (interactivité JS) est le cœur de Symfony UX. Le PHP gère les données et la structure, JavaScript gère uniquement ce qui ne peut pas se faire côté serveur.
Exemple pratique
Création d'un composant Quiz complet
Cet exemple crée un composant Quiz réutilisable qui affiche une série de questions à choix multiple et révèle la bonne réponse au clic, grâce à Stimulus.
Étape 1 — Installer ux-twig-component
composer require symfony/ux-twig-component
Étape 2 — Créer la classe PHP du composant
Créer le fichier src/Twig/Components/Quiz.php :
<?php
namespace App\Twig\Components;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
class Quiz
{
public string $titre = 'Quiz';
/** @var array<array{question: string, options: string[], answer: string}> */
public array $questions = [];
public function getNombreQuestions(): int
{
return count($this->questions);
}
}
Étape 3 — Créer le template Twig du composant
Créer le fichier templates/components/Quiz.html.twig :
<div class="quiz-wrapper" data-controller="quiz">
<h2>{{ titre }}</h2>
<p><em>{{ this.getNombreQuestions() }} question(s)</em></p>
{% for index, q in questions %}
<div class="quiz-question" data-quiz-target="question" style="margin-bottom: 1.5rem; padding: 1rem; border: 1px solid #ddd; border-radius: 6px;">
<p><strong>{{ index + 1 }}. {{ q.question }}</strong></p>
{% for option in q.options %}
<label style="display: block; margin: 0.3rem 0; cursor: pointer;">
<input
type="radio"
name="question_{{ index }}"
value="{{ option }}"
data-answer="{{ q.answer }}"
data-action="change->quiz#selectionner"
data-quiz-target="option"
>
{{ option }}
</label>
{% endfor %}
<p data-quiz-target="feedback{{ index }}" style="margin-top: 0.5rem; font-weight: bold;"></p>
</div>
{% endfor %}
</div>
Étape 4 — Créer le contrôleur Stimulus
Créer le fichier assets/controllers/quiz_controller.js :
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
selectionner(event) {
const input = event.target;
const reponseChoisie = input.value;
const bonnereponse = input.dataset.answer;
// Trouver l'index de la question via le nom du champ radio
const nom = input.name; // ex: "question_0"
const index = nom.split('_')[1];
// Trouver le paragraphe feedback correspondant
const feedbackTarget = this.element.querySelector(
`[data-quiz-target="feedback${index}"]`
);
if (!feedbackTarget) return;
if (reponseChoisie === bonnereponse) {
feedbackTarget.textContent = '✓ Bonne réponse !';
feedbackTarget.style.color = 'green';
} else {
feedbackTarget.textContent = `✗ Mauvaise réponse. La bonne réponse était : ${bonnereponse}`;
feedbackTarget.style.color = 'red';
}
// Désactiver tous les boutons radio de cette question
const radios = this.element.querySelectorAll(`input[name="${nom}"]`);
radios.forEach(r => r.disabled = true);
}
}
Étape 5 — Utiliser le composant dans un contrôleur Symfony
Créer un contrôleur de test :
php bin/console make:controller QuizDemoController
Modifier src/Controller/QuizDemoController.php :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class QuizDemoController extends AbstractController
{
#[Route('/quiz-demo', name: 'app_quiz_demo')]
public function index(): Response
{
$questions = [
[
'question' => 'Quel attribut PHP active un Twig Component ?',
'options' => ['#[Component]', '#[AsTwigComponent]', '#[TwigComponent]', '#[UxComponent]'],
'answer' => '#[AsTwigComponent]',
],
[
'question' => 'Où place-t-on les classes PHP des Twig Components ?',
'options' => ['src/Components/', 'src/Twig/Components/', 'src/UX/', 'templates/components/'],
'answer' => 'src/Twig/Components/',
],
[
'question' => 'Quel préfixe évalue une expression Twig dans un attribut de composant ?',
'options' => ['@', '$', '!', ':'],
'answer' => ':',
],
];
return $this->render('quiz_demo/index.html.twig', [
'questions' => $questions,
]);
}
}
Étape 6 — Appeler le composant dans le template
Modifier templates/quiz_demo/index.html.twig :
{% extends 'base.html.twig' %}
{% block title %}Démo Quiz{% endblock %}
{% block body %}
<div style="max-width: 700px; margin: 2rem auto; padding: 0 1rem;">
<h1>Démonstration du composant Quiz</h1>
<twig:Quiz
titre="Quiz Symfony UX"
:questions="questions"
/>
</div>
{% endblock %}
Étape 7 — Tester
symfony server:start
Ouvrir https://127.0.0.1:8000/quiz-demo. Chaque question affiche ses options sous forme de boutons radio. Au clic sur une option, le feedback s'affiche immédiatement en vert ou en rouge, et les options sont désactivées pour empêcher un second choix.
Le composant est entièrement réutilisable. Pour afficher un second quiz sur la même page, il suffit d'appeler <twig:Quiz titre="Quiz 2" :questions="autresQuestions" /> avec un autre tableau de questions.
Test de mémorisation/compréhension
TP "Alerte de notification Symfony UX"
Dans ce TP, vous allez créer un composant Twig réutilisable nommé Alerte, capable d'afficher des messages de notification colorés (succès, erreur, avertissement, info). Un bouton de fermeture animé, géré par un contrôleur Stimulus, permettra de masquer l'alerte avec une transition d'opacité. Le composant sera intégré dans une page de démonstration.
Étape 1 — Créer le projet et installer les dépendances
Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :
cd %USERPROFILE%\Documents
symfony new tp-alerte-notification --webapp
cd tp-alerte-notification
code .
Une fois VS Code ouvert sur le projet, installez le package ux-twig-component :
composer require symfony/ux-twig-component
Vérifiez que l'installation s'est bien déroulée en cherchant symfony/ux-twig-component dans composer.json.
Après chaque composer require, tapez git diff dans le terminal pour voir exactement quels fichiers ont été modifiés par la recette Flex. C'est le moyen le plus fiable de comprendre ce que Flex a configuré automatiquement (mise à jour de controllers.json, de importmap.php, création de fichiers de configuration, etc.).
Étape 2 — Créer la classe PHP du composant Alerte
Créez le fichier src/Twig/Components/Alerte.php (créez le dossier Twig/Components si nécessaire).
Ce composant doit exposer :
- Une propriété
type(string,'info'par défaut) : détermine la couleur de l'alerte - Une propriété
message(string,''par défaut) : le texte à afficher - Une propriété
dismissible(bool,truepar défaut) : affiche ou non le bouton de fermeture - Une méthode
getCssClass(): retourne la classe CSS Bootstrap correspondant au type
Définissez toujours des valeurs par défaut cohérentes pour les propriétés publiques d'un composant. Ici, type = 'info' et dismissible = true garantissent qu'un <twig:Alerte /> appelé sans aucun attribut reste visuellement correct. Un composant sans valeurs par défaut oblige l'appelant à fournir tous les attributs — ce qui le rend fragile.
Étape 3 — Créer le template Twig du composant
Créez le fichier templates/components/Alerte.html.twig.
Le template doit :
- Positionner
data-controller="alerte"directement sur la div de l'alerte (permet à Stimulus d'agir sur l'alerte entière viathis.element) - Utiliser
this.getCssClass()pour les classes CSS dynamiques - Afficher le message dans un
<span> - Afficher conditionnellement un bouton de fermeture avec
data-action="click->alerte#fermer"
Quand le contrôleur Stimulus doit agir sur l'élément entier (le masquer, le supprimer, l'animer), placez data-controller sur cet élément directement. this.element y référence automatiquement. Réservez static targets aux éléments internes à retrouver parmi plusieurs descendants (champ de texte, compteur, zone de feedback, etc.).
Étape 4 — Créer le contrôleur Stimulus avec animation
Créez le fichier assets/controllers/alerte_controller.js.
La méthode fermer() doit animer la disparition de l'alerte en trois temps :
- Activer une transition CSS
opacity 0.3ssurthis.element - Mettre l'opacité à
0(déclenche l'animation) - Supprimer l'élément du DOM après 300 ms (durée de l'animation)
La durée du setTimeout doit correspondre exactement à la durée de la transition CSS. Si la transition est 0.3s (300 ms), le setTimeout doit valoir 300. Extraire cette valeur dans une constante (const DUREE_MS = 300) évite les bugs de désynchronisation si on change la durée de l'animation.
Étape 5 — Créer le contrôleur Symfony et la page de démonstration
Générez un contrôleur de démonstration :
php bin/console make:controller AlerteController
Modifiez src/Controller/AlerteController.php pour préparer un tableau de quatre alertes de types différents et les transmettre au template :
Le contrôleur Symfony ne doit jamais référencer directement un composant Twig. Son rôle est de préparer des données structurées et de les transmettre au template. C'est le template qui décide du rendu, y compris en déléguant à des composants réutilisables. Cette séparation facilite les tests unitaires du contrôleur et le remplacement du composant sans toucher au contrôleur.
Étape 6 — Vérifier la configuration et tester
Ouvrez templates/base.html.twig et vérifiez la présence de {{ importmap('app') }} dans le <head>. Lancez ensuite le serveur :
symfony server:start
Accédez à https://127.0.0.1:8000/alerte et vérifiez :
- Les quatre alertes s'affichent avec des couleurs distinctes selon leur type
- Un clic sur
×déclenche une animation d'opacité puis supprime l'alerte - La cinquième alerte (non fermable) n'affiche pas de bouton
× - La console navigateur (
F12) ne contient aucune erreur JavaScript
Pour déboguer un composant Twig, l'outil le plus rapide est l'inspecteur du navigateur (F12 → Elements). Le HTML généré révèle exactement ce que Symfony UX a produit : les attributs data-*, les classes CSS, la structure. Si data-controller est absent, le problème est dans le template Twig. Si le contrôleur est présent mais ne réagit pas, le problème est côté JavaScript (nom de fichier, importmap).
Une solution complète du TP
Une solution
Vous devez être connecté pour voir le contenu.
TP "AvisUtilisateur Symfony UX"
Dans ce TP, vous allez créer un composant Twig réutilisable nommé AvisUtilisateur,
capable d'afficher une note sous forme d'étoiles colorées (jaunes pour les étoiles actives, grises pour les inactives) accompagnée d'un commentaire.
Le composant sera ensuite utilisé dans une page de démonstration affichant plusieurs avis sur un produit fictif.
Étape 1 — Créer le projet et installer les dépendances
Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :
cd %USERPROFILE%\Documents
symfony new tp-avis-utilisateur --webapp
cd tp-avis-utilisateur
code .
Une fois VS Code ouvert sur le projet, installez le package ux-twig-component :
composer require symfony/ux-twig-component
Vérifiez que l'installation s'est correctement déroulée en cherchant symfony/ux-twig-component dans le fichier composer.json.
Après chaque composer require, vérifiez toujours que le package apparaît dans composer.json ET que Symfony Flex a bien exécuté sa recette (message dans le terminal). La recette peut créer des fichiers de configuration automatiquement — vérifiez également config/bundles.php pour confirmer l'enregistrement du bundle.
Étape 2 — Créer la classe PHP du composant AvisUtilisateur
Créez le fichier src/Twig/Components/AvisUtilisateur.php.
Ce composant doit exposer les propriétés et méthodes suivantes :
- Une propriété publique
auteurde typestring, valeur par défaut'Anonyme' - Une propriété publique
notede typeint, valeur par défaut5(valeur entre 1 et 5) - Une propriété publique
commentairede typestring, valeur par défaut'' - Une propriété publique
datede typestring, valeur par défaut'' - Une méthode publique
getEtoiles()qui retourne un tableau de 5 éléments, chacun étant un tableau associatif avec une cléactive(booléen) indiquant si l'étoile doit être jaune ou grise
Dans un Twig Component, les propriétés publiques constituent l'interface publique du composant — elles sont directement accessibles dans le template Twig sans configuration supplémentaire. Définissez toujours des valeurs par défaut sensées pour rendre le composant utilisable même sans paramètres obligatoires. Réservez les méthodes publiques aux calculs dérivés (comme getEtoiles()), appelables via this.methode() dans Twig.
Étape 3 — Créer le template Twig du composant
Créez le fichier templates/components/AvisUtilisateur.html.twig. Ce template doit afficher :
- Le nom de l'auteur
- Les 5 étoiles (étoile pleine
★en jaune si active, en gris clair si inactive) - La note sous forme textuelle, par exemple
3 / 5 - Le commentaire de l'utilisateur
- La date de l'avis (si elle est renseignée)
Le tout doit être contenu dans une <div> avec un style carte (bordure, arrondi, ombre légère).
Dans un Twig Component, les propriétés publiques s'accèdent directement ({{ auteur }}), tandis que les méthodes publiques nécessitent le préfixe this. ({{ this.getEtoiles() }}). Utilisez les méthodes pour les calculs dérivés ou les transformations de données — gardez les propriétés pour les données brutes transmises par l'appelant.
Étape 4 — Créer le contrôleur Symfony
Générez un contrôleur nommé AvisController :
php bin/console make:controller AvisController
Modifiez src/Controller/AvisController.php pour préparer un tableau de cinq avis fictifs sur un produit et le transmettre au template. Chaque avis doit contenir les clés auteur, note, commentaire et date.
Le contrôleur Symfony ne doit pas connaître l'existence des composants Twig utilisés dans ses templates. Son rôle est de préparer et transmettre des données structurées. C'est le template qui décide comment afficher ces données, y compris en délégant à des composants. Cette séparation facilite la réutilisation et les tests unitaires du contrôleur.
Étape 5 — Créer le template de la page
Modifiez templates/avis/index.html.twig pour itérer sur le tableau avis et appeler le composant <twig:AvisUtilisateur /> pour chaque entrée. Ajoutez également un en-tête affichant le nom du produit et la note moyenne calculée en Twig.
Dans les attributs d'un composant Twig, le préfixe : est indispensable pour passer des valeurs dynamiques : :note="a.note" évalue l'expression Twig et passe la valeur entière, tandis que note="a.note" passerait littéralement la chaîne "a.note". Utilisez le préfixe : pour toute variable, accès à un tableau ou expression booléenne.
Étape 6 — Vérifier la balise importmap et tester
Ouvrez templates/base.html.twig et vérifiez que la balise suivante est bien présente dans le <head> :
{{ importmap('app') }}
Lancez ensuite le serveur de développement :
symfony server:start
Accédez à https://127.0.0.1:8000/avis et vérifiez les points suivants :
- La note moyenne et les étoiles du résumé global s'affichent correctement en haut de page
- Chaque avis affiche le bon nombre d'étoiles jaunes (les étoiles inactives sont grises)
- L'avis avec une note de 2 affiche bien 2 étoiles jaunes et 3 grises
- L'avis avec une note de 5 affiche bien 5 étoiles jaunes
- Le composant est visuellement cohérent pour chaque avis (carte avec bordure et ombre)
La fonction {{ dump() }} de Twig est votre meilleur allié pour déboguer un composant. Utilisez {{ dump(this.methode()) }} pour inspecter le retour d'une méthode, ou {{ dump(maPropriete) }} pour une propriété. Pensez toujours à retirer ces instructions avant de passer en production — elles exposent la structure interne de votre application.
Une solution complète du TP
Une solution
Vous devez être connecté pour voir le contenu.
TP "Quiz Symfony UX"
Dans ce TP, vous allez créer de A à Z un composant Twig réutilisable nommé Alerte, capable d'afficher des messages de différents types (succès, erreur, avertissement, info), puis vous l'utiliserez dans une page de démonstration. Vous ajouterez ensuite un comportement de fermeture via un contrôleur Stimulus.
Étape 1 — Créer le projet et installer les dépendances
Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :
cd %USERPROFILE%\Documents
symfony new tp-twig-component --webapp
cd tp-twig-component
code .
Une fois VS Code ouvert, installez le package ux-twig-component :
composer require symfony/ux-twig-component
Vérifiez que l'installation s'est bien passée en cherchant symfony/ux-twig-component dans le fichier composer.json à la racine du projet.
Le flag --webapp crée un projet Symfony complet avec Twig, AssetMapper, Stimulus et les outils de développement préconfigurés. C'est le point de départ recommandé pour tout projet web. Pour un microservice API sans interface, préférez symfony new mon-projet sans flag.
Étape 2 — Créer la classe PHP du composant Alerte
Créez le dossier src/Twig/Components/ s'il n'existe pas encore, puis créez-y le fichier Alerte.php.
Ce composant doit avoir :
- Une propriété publique
typede typestring, avec la valeur par défaut'info' - Une propriété publique
messagede typestring, avec la valeur par défaut'' - Une propriété publique
dismissiblede typebool, avec la valeur par défauttrue - Une méthode publique
getCssClass()qui retourne une classe CSS selon le type :'success'→'alert-success''error'→'alert-danger''warning'→'alert-warning'- tout autre type →
'alert-info'
L'expression match de PHP 8.0 est plus concise et plus sûre que switch : elle utilise une comparaison stricte (===), retourne une valeur directement, et lève une UnhandledMatchError si aucun cas ne correspond (contrairement à switch qui passe silencieusement). Utilisez match pour les mappages simples valeur → valeur.
Étape 3 — Créer le template Twig du composant
Créez le fichier templates/components/Alerte.html.twig. Ce template doit :
- Afficher une
<div>avec les classesalertet la classe retournée pargetCssClass() - Afficher le message dans la div
- Si
dismissibleest vrai, afficher un bouton de fermeture<button>avecdata-action="click->alerte#fermer" - Entourer le tout d'un
<div data-controller="alerte">pour permettre à Stimulus d'agir
La convention de nommage Stimulus pour les cibles est data-[nom-contrôleur]-target="[nom-cible]". Si le contrôleur s'appelle alerte et la cible boite, l'attribut est data-alerte-target="boite". Dans le contrôleur JS, Stimulus génère automatiquement this.boiteTarget (singulier) et this.boiteTargets (pluriel si plusieurs éléments).
Étape 4 — Créer le contrôleur Stimulus pour la fermeture
Créez le fichier assets/controllers/alerte_controller.js. Ce contrôleur doit :
- Déclarer une cible
boite - Exposer une méthode
fermer()qui masque l'élément cibleboiteen lui appliquantdisplay: none
La convention Stimulus est nom_controller.js avec des underscores. Le nom du contrôleur (utilisé dans data-controller) est déduit du nom de fichier en retirant _controller.js. Ainsi alerte_controller.js → data-controller="alerte". Les tirets sont aussi supportés : mon-composant_controller.js → data-controller="mon-composant".
Étape 5 — Créer le contrôleur Symfony et la page de démonstration
Générez un contrôleur Symfony nommé AlerteDemoController :
php bin/console make:controller AlerteDemoController
Modifiez src/Controller/AlerteDemoController.php pour passer au template un tableau de messages avec leurs types :
$alertes = [
['type' => 'success', 'message' => 'L\'opération a été réalisée avec succès.'],
['type' => 'error', 'message' => 'Une erreur critique est survenue.'],
['type' => 'warning', 'message' => 'Attention, cette action est irréversible.'],
['type' => 'info', 'message' => 'Une mise à jour est disponible.'],
];
Transmettez ce tableau au template via render().
Depuis Symfony 6, la syntaxe d'attribut PHP #[Route('/chemin', name: 'nom_route')] remplace les annotations Doctrine @Route. Toujours nommer ses routes avec name: — cela permet d'utiliser $this->generateUrl('nom_route') ou path('nom_route') en Twig, découplant le code des URLs réelles.
Étape 6 — Appeler le composant dans le template
Modifiez templates/alerte_demo/index.html.twig pour itérer sur le tableau alertes et appeler le composant <twig:Alerte /> pour chaque entrée. Utilisez le préfixe : pour passer les valeurs dynamiques.
Ne jamais passer des booléens sans le préfixe :. Sans :, dismissible="false" passe la chaîne "false" (valeur truthy en PHP !), pas le booléen false. Avec :dismissible="false", Twig évalue l'expression et passe le vrai booléen false. Idem pour true, null, et les nombres.
Étape 7 — Tester et vérifier le comportement
Vérifiez d'abord la présence de {{ importmap('app') }} dans templates/base.html.twig, puis lancez le serveur :
symfony server:start
Accédez à https://127.0.0.1:8000/alerte-demo. Vérifiez les points suivants :
- Les quatre alertes s'affichent avec des couleurs différentes selon leur type
- Cliquer sur le bouton
×fait disparaître l'alerte sans rechargement de page - La cinquième alerte (non fermable) n'affiche pas de bouton
× - Ouvrir la console du navigateur (
F12) et vérifier l'absence d'erreurs JavaScript
La balise {{ importmap('app') }} dans le <head> est le pont entre PHP et JavaScript avec AssetMapper. Elle génère une Import Map HTML qui permet au navigateur de résoudre les imports ES modules sans bundler. Sans elle, aucun contrôleur Stimulus ne se charge. C'est l'équivalent du tag encore_entry_link_tags() de Webpack Encore.
Une solution complète du TP
Une solution
Vous devez être connecté pour voir le contenu.
TP "Slide Symfony UX"
Dans ce TP, vous allez créer un composant Twig réutilisable nommé Slide, capable d'afficher un diaporama de slides navigables grâce à des boutons "Précédent" et "Suivant". La navigation entre les slides sera gérée par un contrôleur Stimulus, sans rechargement de page. Le composant sera ensuite utilisé dans une page de démonstration.
Étape 1 — Créer le projet et installer les dépendances
Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :
cd %USERPROFILE%\Documents
symfony new tp-slide-component --webapp
cd tp-slide-component
code .
Une fois VS Code ouvert sur le projet, installez le package ux-twig-component :
composer require symfony/ux-twig-component
Attendez la fin de l'installation, puis vérifiez que le package est bien présent dans composer.json.
Après symfony new --webapp, vérifiez systématiquement la présence de assets/controllers/ (pour vos contrôleurs Stimulus), templates/ (pour vos templates Twig) et importmap.php (pour les dépendances JavaScript). Ces dossiers et fichiers sont créés automatiquement par Flex et constituent la base de tout projet Symfony UX.
Étape 2 — Créer la classe PHP du composant Slide
Créez le fichier src/Twig/Components/Slide.php. Ce composant représente un diaporama. Il doit exposer :
- Une propriété publique
titrede typestring, avec la valeur par défaut'Diaporama' - Une propriété publique
slidesde typearray, initialisée à un tableau vide. Chaque slide sera un tableau associatif avec les cléstitreetcontenu. - Une méthode publique
getNombreSlides()qui retourne le nombre total de slides sous forme d'entier.
Le commentaire @var array<array{titre: string, contenu: string}> documente la structure attendue de chaque élément du tableau. Sans cela, les IDEs ne peuvent pas proposer l'autocomplétion sur les clés. C'est particulièrement important dans les Twig Components dont les propriétés sont des tableaux complexes transmis depuis des contrôleurs.
Étape 3 — Créer le template Twig du composant
Créez le fichier templates/components/Slide.html.twig. Ce template doit :
- Afficher le titre du diaporama dans un
<h2> - Afficher un compteur de slides (exemple : "Slide 1 / 3") dans un paragraphe utilisant une cible Stimulus
compteur - Itérer sur les slides avec
{% for index, slide in slides %}et afficher chaque slide dans une<div>avecdata-slide-target="panneau" - Masquer par défaut tous les panneaux sauf le premier (via
style="display: none"conditionnel) - Afficher deux boutons de navigation : "Précédent" et "Suivant", chacun avec
data-actionStimulus approprié - Entourer le tout d'un
<div data-controller="slide">
Le système values de Stimulus (static values = { total: Number }) est la méthode recommandée pour passer des données du serveur vers un contrôleur JS. L'attribut HTML data-[controller]-[name]-value est lié à this.[name]Value dans le contrôleur, avec conversion automatique de type (Number, String, Boolean, Array, Object). C'est plus robuste que de lire dataset manuellement.
Étape 4 — Créer le contrôleur Stimulus
Créez le fichier assets/controllers/slide_controller.js. Ce contrôleur doit :
- Déclarer les cibles
panneauetcompteur - Déclarer une valeur
totalde typeNumber - Maintenir un index courant (initialisé à
0) dans une propriété d'instance - Exposer une méthode
suivant()qui passe à la slide suivante (sans dépasser la dernière) - Exposer une méthode
precedent()qui revient à la slide précédente (sans passer en dessous de 0) - Chaque changement de slide doit masquer l'ancienne, afficher la nouvelle, et mettre à jour le compteur
Évitez de dupliquer la logique de changement d'état entre les méthodes suivant() et precedent(). Créez une méthode interne allerA(index) qui centralise la mise à jour du DOM. Cette approche facilite l'ajout futur d'une navigation directe (clic sur un indicateur de point) sans toucher aux méthodes existantes.
Étape 5 — Créer le contrôleur Symfony et préparer les données
Générez un contrôleur Symfony nommé SlideDemoController :
php bin/console make:controller SlideDemoController
Modifiez src/Controller/SlideDemoController.php pour préparer un tableau de slides sur le thème de Symfony UX et le transmettre au template :
$slides = [
['titre' => '...', 'contenu' => '...'],
// au moins 4 slides
];
Chaque slide doit avoir un titre court et un contenu de 1 à 2 phrases.
Utilisez toujours php bin/console make:controller NomController plutôt que de créer les fichiers manuellement. Cette commande crée automatiquement la classe contrôleur avec le bon namespace, l'action index() avec sa route, ET le template associé dans templates/. Cela évite les erreurs de nommage et respecte les conventions Symfony.
Étape 6 — Appeler le composant dans le template
Modifiez templates/slide_demo/index.html.twig pour appeler le composant <twig:Slide /> en lui passant le titre du diaporama et le tableau de slides.
Un composant est véritablement réutilisable quand on peut l'instancier plusieurs fois sur la même page avec des données différentes, chaque instance étant totalement indépendante. Testez toujours cette réutilisabilité en ajoutant une deuxième instance dans votre template de test — si elles interfèrent, votre contrôleur Stimulus a probablement un état global au lieu d'un état d'instance.
Étape 7 — Vérifier la balise importmap et tester
Ouvrez templates/base.html.twig et vérifiez que la balise suivante est bien présente dans le <head> :
{{ importmap('app') }}
Lancez ensuite le serveur de développement et testez le composant dans le navigateur :
symfony server:start
Accédez à https://127.0.0.1:8000/slide-demo et vérifiez :
- La première slide s'affiche au chargement, les autres sont masquées
- Le compteur affiche "Slide 1 / 5"
- Le bouton "Suivant" avance à la slide suivante et met à jour le compteur
- Le bouton "Précédent" revient en arrière
- Les boutons ne dépassent pas les limites (pas de slide 0 ni de slide 6)
- La console du navigateur (
F12) ne contient aucune erreur JavaScript
Chaque instance d'un contrôleur Stimulus est liée à un élément DOM distinct avec data-controller. Les propriétés d'instance comme indexCourant = 0 sont propres à chaque instance — deux diaporamas sur la même page ont chacun leur propre état. C'est pourquoi Stimulus est préféré à du JavaScript global qui partagerait un état commun entre instances.
Une solution complète du TP
Une solution
Vous devez être connecté pour voir le contenu.