Templates modernes avec l'aide de Gemini CLI
Comment transformer un template Twig généré automatiquement (austère) en une page moderne et conviviale, en se faisant aider par un agent IA en ligne de commande, sans jamais risquer de casser le projet

Petite vérification
Avant de commencer, vérifiez si vous savez...
- citer les trois options de
CollectionTypequi autorisent l'ajout dynamique de réponses, leur suppression, et qui forcent Symfony à passer paraddAnswer()/removeAnswer() - expliquer pourquoi
php bin/console make:crud Questionn'a généré aucun champ pour la collection$answersdans ses formulaires - dans le template
templates/question/show.html.twiggénéré parmake:crud, citer les deux seuls champs deQuestionqui sont actuellement affichés
📌 Réponse attendue
Notions théoriques
Le problème : un CRUD fonctionnel, mais un rendu austère et incomplet
La commande make:crud Question, utilisée dans la séance précédente, a généré ce template pour afficher une question :
{# templates/question/show.html.twig (généré automatiquement, tel quel) #}
{% extends 'base.html.twig' %}
{% block title %}Question{% endblock %}
{% block body %}
<h1>Question</h1>
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ question.id }}</td>
</tr>
<tr>
<th>Text</th>
<td>{{ question.text }}</td>
</tr>
<tr>
<th>Explication</th>
<td>{{ question.explication }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_question_index') }}">back to list</a>
<a href="{{ path('app_question_edit', {'id': question.id}) }}">edit</a>
{{ include('question/_delete_form.html.twig') }}
{% endblock %}
Deux problèmes sautent aux yeux :
- Les réponses ne sont pas affichées du tout.
make:crudne génère jamais de code pour une relationOneToMany: il faudra l'ajouter nous-mêmes, comme pour le formulaire de la séance précédente. - La présentation est un simple tableau HTML brut, avec des libellés en anglais (
Id,Text) : rien qui ressemble à l'écran d'un quiz que passerait un vrai utilisateur.
On va corriger les deux problèmes : afficher les réponses, et donner à cette page une présentation moderne, pensée pour un quiz (carte centrée, boutons de réponse, mise en évidence de la bonne réponse). Pour la partie visuelle, plutôt que de tout dessiner à la main en CSS, on va se faire aider par Gemini CLI, un agent IA qui s'exécute dans le terminal.
Qu'est-ce que Gemini CLI ?
Gemini CLI est un outil en ligne de commande, développé par Google, qui donne accès au modèle d'IA Gemini directement depuis le terminal. Contrairement à un simple chatbot dans un navigateur, Gemini CLI est un agent : il peut lire des fichiers, en écrire de nouveaux, et exécuter des commandes dans le dossier où il est lancé.
C'est justement parce que Gemini CLI peut écrire et modifier des fichiers tout seul qu'il faut apprendre à l'utiliser prudemment, avant même d'apprendre à écrire un bon prompt. Un agent mal encadré pourrait, par exemple, écraser un fichier existant du projet Symfony en croyant bien faire. Les règles de sécurité ci-dessous ne sont pas facultatives.
Installer Gemini CLI
Gemini CLI est un paquet Node.js, installable avec npm :
npm install -g @google/gemini-cli
Pour installer Nodes.js
Pour Installer Node.js, consultez le tutoriel : Installer Node.js
Une fois installé, la commande gemini est disponible dans tout le terminal. Pour vérifier l'installation :
gemini --version
Erreur 'Rename failed with EPERM'
Si l'erreur 'Rename failed with EPERM' s'affiche, veuillez exécuter la commande gemini --version dans un dossier dans vos Documents.
S'authentifier auprès de Gemini
Avant de pouvoir poser la moindre question, Gemini CLI a besoin d'une clé d'API. Sans elle, voici exactement le message d'erreur qui s'affiche :
Please set an Auth method in your ~/.gemini/settings.json or specify one of the
following environment variables before running: GEMINI_API_KEY, GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_GENAI_USE_GCA
Pour un usage simple en classe, la méthode la plus directe est une clé d'API personnelle, gratuite, obtenue sur aistudio.google.com, puis déclarée comme variable d'environnement :
Obtenir une clé d'API AI Studio
Voici les étapes actuelles pour créer une clé API Gemini personnelle avec Google AI Studio :
-
Ouvrez Google AI Studio :
https://aistudio.google.com/ -
Connectez-vous avec votre compte Google.
-
Acceptez les conditions d’utilisation si Google vous les demande.

- Dans le menu ou le 'Tableau de bord' (Dashboard), ouvrez 'Clé d'API' (API Keys).

- Cliquez sur 'Créer une clé API' (Create API key).

- Créer un projet Google Cloud :
- pour un nouveau compte, AI Studio peut créer automatiquement un projet par défaut ;
- si vous avez déjà des projets Google Cloud, choisissez-en un ou cliquez sur Import projects pour l’importer dans AI Studio.



- Cliquez sur Create key, puis copiez immédiatement la clé affichée. Chaque clé API est associée à un projet Google Cloud.



La clé peut être utilisée avec le quota gratuit disponible pour Gemini API.
Pour l’utiliser avec Gemini CLI sous Windows PowerShell :
$env:GEMINI_API_KEY="VOTRE_CLE_API"
gemini
Pour la conserver entre les sessions :
[Environment]::SetEnvironmentVariable(
"GEMINI_API_KEY",
"VOTRE_CLE_API",
"User"
)
Fermez puis rouvrez PowerShell après cette commande.
Sous Linux ou macOS :
export GEMINI_API_KEY="VOTRE_CLE_API"
gemini
Une clé d'API est une information secrète, personnelle, au même titre qu'un mot de passe. Elle ne doit jamais apparaître dans un fichier versionné avec Git (pas de clé en dur dans un script, pas de capture d'écran partagée). La déclarer avec export dans le terminal, ou dans un fichier .env ajouté au .gitignore, sont les deux bonnes pratiques.
Lancer Gemini CLI
Gemini CLI propose 2 façons de fonctionner :
gemini

Lance le mode interactif : une conversation continue s'ouvre dans le terminal.
gemini -p "Saisir ici votre prompt"
Lance le mode non interactif (ou « headless ») : Gemini exécute une seule instruction (-p, pour prompt), affiche sa réponse, puis rend la main.
C'est ce mode qu'on utilisera pour cette séance : une seule demande précise, un résultat, terminé.
Le réglage le plus important : --approval-mode
Avant d'écrire la moindre ligne dans un fichier, Gemini CLI demande en principe une confirmation.
Ce comportement se règle avec l'option --approval-mode, qui accepte quatre valeurs :
| Valeur | Comportement |
|---|---|
default | Demande une confirmation avant chaque action (écriture de fichier, commande shell...). Réglage à utiliser dans cette séance. |
auto_edit | Approuve automatiquement les modifications de fichiers, mais demande confirmation pour le reste. |
yolo | Approuve tout, automatiquement, sans jamais demander. |
plan | Mode lecture seule : Gemini peut analyser, mais ne peut rien écrire ni exécuter. |
--yolo (ni l'option courte -y)Le mode yolo autorise l'agent à écrire, modifier ou supprimer n'importe quel fichier accessible, sans demander votre accord. Son nom (You Only Live Once) résume bien le risque : une seule erreur de compréhension du prompt, et un fichier important peut être écrasé ou supprimé sans confirmation. Pour cette séance (et par prudence, en général), on reste systématiquement en mode default : chaque écriture de fichier proposée par Gemini doit être lue et validée une par une.
2 règles de sécurité à respecter, avant même d'écrire le prompt
Avant de penser au contenu du prompt, voici 2 précautions à prendre pour garantir une sécurité :
- Ne jamais lancer
geminidepuis la racine du projet Symfony. Si l'agent a accès à tout le projet (entités, formulaires, configuration, base de données), la moindre ambiguïté dans le prompt peut l'amener à vouloir « corriger » ou « compléter » un fichier existant. On crée donc un dossier vide, dédié, en dehors du projet, et on y lance Gemini :Dans ce dossier vide, même si l'agent se trompait, il n'y a rien d'important à casser.mkdir ~/maquette-quizcd ~/maquette-quizgemini -p "Saisir ici votre prompt" --approval-mode default - Garder
--approval-mode default(vu ci-dessus) : chaque fichier que Gemini propose de créer est affiché à l'écran avant d'être réellement écrit sur le disque. On lit, on v érifie que ça correspond bien à la demande, puis on accepte.
Rédiger un prompt qui ne produit que 2 fichiers, et rien d'autre
Un bon prompt pour ce genre de demande est précis sur ce qu'il faut faire, et explicite sur ce qu'il ne faut pas faire :
Il faut créer uniquement 2 fichiers dans ce dossier :
- quiz.html
- quiz.css
Il ne faut pas créer d'autre fichier.
Il ne faut pas modifier ni supprimer un fichier existant.
quiz.html doit être une page HTML autonome qui affiche une question de quiz avec
4 réponses possibles, sous forme de boutons ou de cartes cliquables, dans le style
d'une application de quiz moderne (comme Kahoot) :
- il faut utiliser un contenu d'exemple : question "Quelle est la capitale de la France ?", réponses "Paris", "Lyon", "Marseille", "Toulouse".
- il faut ajouter une zone pour afficher une explication sous la question.
- il faut relier quiz.html à quiz.css avec une balise <link>.
quiz.css doit contenir uniquement du CSS moderne, sans framework externe (pas de
Bootstrap, pas de Tailwind) :
- dégradé de couleurs,
- coins arrondis,
- une légère ombre et un effet de survol sur chaque bouton de réponse.
Il faut prévoir une classe CSS nommée "correct" pour mettre en évidence visuellement une bonne réponse.
Décortiquons pourquoi ce prompt est sûr :
- « Il faut crér uniquement 2 fichiers (...). Il ne faut pas créer d'autre fichier. » : élimine toute ambiguïté sur le nombre et le nom des fichiers attendus. Si Gemini (en mode
default) proposait un troisième fichier ou un nom différent, ce serait immédiatement suspect, à refuser. - « Il ne faut pas modifier ni supprimer un fichier existant. » : rappelle explicitement l'interdiction, même si le dossier est déjà vide (bonne habitude à prendre, utile aussi le jour où ce même prompt serait relancé dans un dossier non vide par erreur).
- Un contenu d'exemple fourni (la question sur la capitale de la France) : sans exemple concret, l'IA inventerait un contenu différent à chaque essai, rendant le résultat difficile à comparer d'un étudiant à l'autre.
- Une classe CSS nommée à l'avance (
correct) : c'est vous, dans l'étape suivante, qui déciderez dans le template Twig quelle réponse mérite cette classe ({{ answer.isCorrect ? 'correct' : '' }}). Le mockup HTML/CSS ne doit fournir que l'apparence, jamais la logique : celle-ci reste écrite par vous, dans Twig.
Du mockup statique au template Twig : ce qui change, et ce qui ne change pas
Le fichier quiz.html généré par Gemini contient du HTML statique : une seule question écrite en dur, 4 réponses écrites en dur.
Il ne connaît ni Twig, ni vos entités Question/Answer. Rien dans quiz.html ne doit être copié tel quel dans le projet :
il faut distinguer, dans sa structure HTML, ce qui restera identique (la structure des balises, les classes CSS)
de ce qui doit devenir dynamique (le texte de la question, la liste des réponses).
Prenons un exemple de résultat typique que pourrait produire Gemini pour quiz.html :
<div class="quiz-card">
<h1 class="quiz-question">Quelle est la capitale de la France ?</h1>
<div class="quiz-answers">
<button class="quiz-answer">Paris</button>
<button class="quiz-answer">Lyon</button>
<button class="quiz-answer">Marseille</button>
<button class="quiz-answer">Toulouse</button>
</div>
<p class="quiz-explication">La capitale de la France est Paris depuis le Moyen Âge.</p>
</div>
Pour l'intégrer dans templates/question/show.html.twig, le travail consiste à remplacer le contenu écrit en dur par des variables et une boucle Twig, en conservant exactement les mêmes classes CSS :
<div class="quiz-card">
<h1 class="quiz-question">{{ question.text }}</h1>
<div class="quiz-answers">
{% for answer in question.answers %}
<button class="quiz-answer {{ answer.isCorrect ? 'correct' : '' }}">{{ answer.text }}</button>
{% endfor %}
</div>
{% if question.explication %}
<p class="quiz-explication">{{ question.explication }}</p>
{% endif %}
</div>
Trois transformations, à bien repérer :
"Quelle est la capitale de la France ?"(texte fixe) devient{{ question.text }}(variable).- Les 4
<button>écrits en dur deviennent un seul<button>, répété par{% for answer in question.answers %}: le nombre de réponses affichées dépendra alors réellement du nombre de réponses en base, pas d'un nombre figé à 4. - La classe
correctn'est plus écrite en dur sur un bouton : elle est ajoutée conditionnellement avec l'opérateur ternaire de Twig, uniquement sianswer.isCorrectvauttrue.
Le fichier quiz.css, lui, ne change pas du tout : on le copie tel quel dans public/css/quiz.css, et on l'inclut dans le template avec une balise <link>, exactement comme Bootstrap est déjà inclus dans base.html.twig.
Veuillez penser à placer le fichier quiz.css dans public/css et à le lier avec le template.
- Dans notre application, il ne s'agit pas d'une évaluation, la lecture du code source de la page peut permettre de deviner la bonne réponse.
- Un vrai quiz, destiné à une évaluation, ne doit jamais révéler la bonne réponse avant que l'élève ait répondu.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Votre défi pour aujourd'hui consiste à générer une maquette moderne avec Gemini CLI, puis à l'intégrer, en toute sécurité, dans le template
question/show.html.twig.
Étape 1 — Installer Gemini CLI
Après l'installation, exécutez gemini --version. Si la commande échoue (command not found), le dossier global de npm n'est probablement pas dans votre PATH : mieux vaut corriger ce problème immédiatement plutôt que de continuer en espérant qu'il se résolve tout seul.
Étape 2 — Créer un dossier de travail isolé, hors du projet Symfony
Ne créez jamais ce dossier à l'intérieur du projet Symfony (par exemple dans public/ ou var/) : même isolé dans un sous-dossier, l'agent pourrait détecter la présence du projet Symfony autour de lui et tenter d'en tenir compte. Un dossier complètement extérieur au projet (ici, dans votre répertoire personnel) élimine ce risque.
Étape 3 — Lancer Gemini CLI avec le bon réglage de sécurité
En mode default, Gemini CLI affiche le contenu de chaque fichier avant de l'écrire, et attend votre validation (habituellement en appuyant sur une touche, selon l'interface proposée par la version installée). Prenez le temps de lire ce contenu : c'est précisément ce moment de lecture qui remplace la sécurité qu'offrirait un mode automatique.