- Accueil
- Programmation Web S5
- Objectifs de la séance
- Préambule
- Mise en place d'une application
Symfony
7.1 - Gestion des versions avec Git
GitHub Copilot
- Création du projet dans
PhpStorm
- Qualité de code et automatisation
- Consignes de codage et de nommage
- Documentation du projet
- Standards de codage PHP avec
PHP CS Fixer
- Qualité de code avec
PHPStan
- Standards de codage Twig avec
Twig CS Fixer
- Mise en place de scripts
Composer
- Automatisation des contrôles de qualité de code avec
GrumPHP
- Installation / configuration des greffons
PhpStorm
- Conteneurisation des services de base de données
- Les annonces dans l'application
- Création d'une entité «
Advertisement
» - Génération de données factices
- Création de la base de données
- Visualisation des annonces
- Désactivation de
Symfony UX Turbo
- Utilisation de
Bootstrap
CDN dans l'interface utilisateur - Formulaire d'édition / création d'une annonce
- Automatisation des dates de création et de mise à jour
- Utilisation du formulaire d'édition / création d'une annonce
- Suppression d'une annonce
- Création d'une entité «
- Catégories d'annonces
- Considérations sur les performances
- Mise en place de tests
- Intégration continue
- Amélioration de l'interface et de l'expérience utilisateur
- Interface utilisateur
- Installation de «
AssetMapper
» - Installation de
Sass
- Installation de
Bootstrap Sass
- Personnalisation du thème de couleurs de
Bootstrap
- Ajout de couleurs de thème dans
Bootstrap
- Personnalisation d'éléments de mise en forme de
Bootstrap
- Optimisation de
Bootstrap
Bootstrap Icons
- Gestion du mode clair/sombre dans
Bootstrap
- Installation de «
- Sécurité
- Commandes de console
- Réalisation d'un système de « likes »
- Introduction d'un flux de travail (« workflow ») pour la gestion des annonces
Objectifs de la séance ¶
- Prendre en main
Symfony
dans sa version 7.1 - Consolider les objectifs du TP de découverte de
Symfony 5
- Utiliser le routage
- Développer des « templates »
Twig
- Écrire des tests fonctionnels
- Utiliser l'ORM
Doctrine
- Gérer des relations entre entités
- Générer des données factices (« fixtures ») avec «
Foundry
» - Utiliser le composant
Form
- Utiliser le composant
Security
- Développer en respectant les bonnes pratiques de
Symfony
- Mettre en place des outils de qualité de code
- Automatiser l'utilisation des outils de qualité de code
- Conteneuriser certains services avec
Docker
Préambule ¶
L'objectif de ce TP est de consolider vos acquis en développement d'applications Symfony
(découverte de Symfony 5
puis API Platform
), mais également de vous faire découvrir de nouvelles possibilités. Le sujet vous donnera des consignes et des pointeurs vers de la documentation et des ressources, vous mettant ainsi dans une situation de réalisation professionnelle. L'accent sera une nouvelle fois mis sur les bonnes pratiques de Symfony
, la qualité de développement et l'automatisation.
Le fil conducteur sera un site de petites annonces, dont les fonctionnalités évolueront au fil de vos nouveaux apprentissages.
Mise en place d'une application Symfony
7.1
¶
Vous allez créer des applications Symfony
de façon conventionnelle, en utilisant l'outil « symfony
» en ligne de commande (« Symfony CLI »). Une nouvelle application Symfony
nécessite un peu plus de 80 Mo de sources PHP pour fonctionner (environ 10.000 fichiers dans un peu moins de 2.000 répertoires !). Ces données seront rapidement agrémentées de plusieurs dizaines de mégaoctets de cache générés par Symfony
.
Vous allez installer Symfony
à l'aide de l'outil « symfony
» en ligne de commande (« Symfony CLI ») et pour cela, vous allez devoir l'installer. Cet outil utilise Composer
dont vous devrez vous assurer du bon fonctionnement.
Quelques considérations liées à la configuration système de l'IUT ¶
Remarque importante
Si vous travaillez sur votre ordinateur personnel, sur Linux ou sur Windows, vous pouvez placer le répertoire « symfony-for-sale
» du projet où bon vous semble. La partie qui suit concerne uniquement le travail sur les ordinateurs de l'IUT. Cela ne vous empêche pas d'en prendre connaissance.
Votre répertoire d'accueil « $HOME
» se trouve sur un emplacement réseau NFS
, ce qui vous permet de retrouver vos fichiers quel que soit le PC du département sur lequel vous vous connectez. Symfony
utilise le sous-répertoire « var
» du projet pour la gestion de son cache, des traces d'exécution et des données de session. Ces tâches génèrent de nombreuses entrées-sorties sur le système de fichiers, particulièrement inadaptées aux systèmes de fichiers en réseau. Il en va de même pour PhpStorm
qui surveille les modifications de tous les fichiers de votre projet et ne peut pas fonctionner normalement lorsque ces fichiers sont sur un partage NFS
. Afin d'améliorer les performances de votre application et d'éviter de saturer le serveur de fichiers du département informatique, vous allez travailler dans le répertoire « /working/votre_login
» de la machine sur laquelle vous êtes connecté.
En fin de séance ¶
En fin de séance, vous devez impérativement synchroniser votre dépôt distant (en ligne de commande ou avec PhpStorm
)
git commitpuis
git pushfaute de quoi vous perdrez votre travail entre chaque séance !
Pensez également à supprimer votre répertoire de travail /working/votre_login/symfony-for-sale
pour éviter la saturation du disque local :
rm -Rf /working/votre_login/symfony-for-sale
En début de séance ¶
Lorsque vous reprenez le travail la séance suivante, vous avez trois choix possibles, qui dépendent de la façon dont vous avez travaillé :
- Mettre à jour votre dépôt local
cd /working/votre_login/symfony-for-sale
git pull
- Effacer votre dépôt local et repartir de zéro
cd /working/votre_login
rm -Rf /working/votre_login/symfony-for-sale
git clone https://iut-info.univ-reims.fr/gitlab/votre_login/symfony-for-sale.git
cd /working/votre_login/symfony-for-sale
- Réinitialiser votre dépôt local selon le dépôt distant
cd /working/votre_login/symfony-for-sale
git fetch
git reset --hard origin/main
Ensuite, dans le répertoire de votre projet, vous devez (ré)installer les composants nécessaires à son fonctionnement :
composer install
Vous devrez également reconfigurer votre accès base de données en redéfinissant le fichier « .env.local
»
Installation / mise à jour de l'exécutable « symfony
» et de Composer
¶
Travail à réaliser
- Vérifiez que l'outil «
symfony
» en ligne de commande (« Symfony CLI ») est installé et à jour en reprenant la procédure d'installation vu l'an passé - Contrôlez la compatibilité du système avec la commande :
symfony check:requirements --verbose
- Vérifiez que «
Composer
» est installé et à jour en reprenant la procédure d'installation vu l'an passé - Vérifiez que
Composer
fonctionne correctement :composer about
- Mettez à jour
Composer
:composer self-update
Création du projet Symfony
¶
Vous allez pouvoir créer un projet Symfony
à l'aide de l'outil « symfony
» en ligne de commande (« Symfony CLI »).
Travail à réaliser
- Vérifiez que vous êtes bien dans le répertoire «
/working/votre_login
» (ou n'importe quel répertoire de votre choix si vous êtes sur votre ordinateur personnel) - Lancez la création d'un nouveau projet
Symfony
version «7.1.*
» :symfony --version 7.1 --webapp new symfony-for-sale
Vérification du bon fonctionnement de l'application ¶
Si vous avez réalisé toutes les étapes précédentes sans encombre, votre application doit fonctionner. Vous allez donc le vérifier. Vous allez utiliser l'environnement de développement de Symfony
qui est celui qui est activé par défaut en mode développement.
Travail à réaliser
- Placez-vous dans le répertoire de votre application
- Dans le terminal, lancez le serveur Web local avec la commande suivante :
symfony serve
Le serveur Web fonctionnera tant que vous n'aurez pas terminé l'exécution de l'outil ligne de commande «symfony
» avec «CTRL+C
».Remarque importanteSi le serveur local ne fonctionne pas correctement en
HTTPS
, installez un certificat local. - Lancez votre navigateur web et saisissez l'URL suivante :
https://127.0.0.1:8000
- Vérifiez que le serveur Web local répond correctement
- Terminez l'exécution de l'outil ligne de commande «
symfony
» avec «CTRL+C
»
Gestion des versions avec Git ¶
Afin de fiabiliser votre méthode de développement, vous allez utiliser Git
pour gérer le suivi des versions de votre application Symfony
.
Dépôt local ¶
Vous allez configurer votre dépôt Git
local.
Travail à réaliser
- Assurez-vous d'être dans le répertoire du projet
symfony-for-sale
- Constatez que le dépôt
Git
local a été initialisé par l'outil «symfony
» en ligne de commande (« Symfony CLI »)git log
GitHub Copilot
¶
Une partie du code que vous produisez est prévisible, au moins en partie. C'est pourquoi GitHub
a développé un outil d'assistance à la saisie de code, GitHub Copilot
, qui s'appuie sur l'intelligence artificielle et l'apprentissage automatique pour vous aider à écrire du code. Cet outil est utilisé en entreprise pour augmenter la productivité des développeurs. Il nous semble donc intéressant de vous le présenter, tout en vous sensibilisant à son utilisation éclairée et raisonnée.
Information
Certaines informations sont nécessaires au bon déroulement de votre inscription au « GitHub Student Developer Pack ». Lisez l'intégralité des consignes fournies dans le sujet avant de vous lancer dans la création de votre compte GitHub
et de vous inscrire au « GitHub Student Developer Pack ».
Travail à réaliser
- Si vous n'en possédez pas déjà un, créez un compte sur
GitHub
, de préférence avec votre mail universitaire - Inscrivez-vous au « GitHub Student Developer Pack » : https://education.github.com/pack
- Si votre mail universitaire n'est pas connu de
GitHub
, vous devez le renseigner, car il est nécessaire pour être reconnu comme étudiant - Fournissez une photo de votre carte d'étudiant ou de votre certificat de scolarité Information
Vous devez impérativement fournir une photo de votre carte d'étudiant ou de votre certificat de scolarité. Vous pouvez utiliser votre smartphone pour obtenir un fichier image au format
JPEG
. Les captures d'écran du certificat de scolarité seront rejetées. - Constatez l'état de votre demande qui devrait être approuvée :
- Patientez quelques jours avant de profiter de votre accès à
GitHub Copilot
! - Après 3 ou 4 jours d'attente, vos avantages sont disponibles :
- Accédez à vos paramètres de compte concernant
GitHub Copilot
- Activez
GitHub Copilot
en cliquant sur le bouton « Start free trial » : - Vous devriez voir apparaître un message donnant accès à la version gratuite de
GitHub Copilot
: - Cliquez sur le bouton « Get access to GitHub Copilot »
- Choisissez ensuite vos préférences de
GitHub Copilot
:
Création du projet dans PhpStorm
¶
La gestion d'une application Symfony
nécessitant de modifier de nombreux fichiers dans une arborescence complexe, vous utiliserez PhpStorm
pour l'édition de texte. Des greffons apportent le support de Symfony
, Composer
, des fichiers YAML
, Twig
, … Certains sont préinstallés dans la configuration générale.
Vous allez créer un nouveau projet PHP
à partir de PhpStorm
pour gérer votre application Web.
Remarque importante
PhpStorm
est un outil puissant parmi d'autres développés par JetBrains. Leur utilisation dans le cadre professionnel impose de s'acquitter d'une licence annuelle. Cependant, dans la cadre universitaire, vous pouvez disposer d'une licence gratuite pour les étudiants. Ceci vous permet d'utiliser les outils sur votre ordinateur personnel, tant que vous êtes étudiant. L'utilisation de PhpStorm
au sein du département ne nécessite pas de démarche particulière puisque nous avons un serveur de licences JetBrains.
Je vous invite néanmoins à demander une licence et à travailler sur votre ordinateur personnel en dehors des séances de TP.
Travail à réaliser
- Dans votre terminal et dans le répertoire du projet, lancez la commande
phpstorm . &
Remarque importanteLa création du projet
PhpStorm
induit la création du répertoire «.idea
» dans le répertoire de votre projetSymfony
. En temps normal, en particulier lors d'un développement collaboratif comme la SAÉ de S3, il est impensable d'inclure le répertoire «.idea
» au dépôtGit
. En effet, chaque modification de la configuration dePhpStorm
faite par l'un des développeurs va engendrer la modification de la configuration de tous les autres développeurs une fois que la modification se retrouvera dans la branche principale.Néanmoins, à cause de la configuration particulière au sein du département informatique qui vous oblige à travailler dans le répertoire local «
/working/votre_login
» sur des ordinateurs différents, et parce que vous êtes l'unique développeur de votre projet, il est légitime d'inclure le répertoire de configuration dePhpStorm
, «.idea
», afin que vous n'ayez pas à reconfigurer votre environnement lorsque vous clonerez votre dépôt en début de séance.
Qualité de code et automatisation ¶
Consignes de codage et de nommage ¶
Le code de l'écosystème Symfony
est intégralement en anglais. Tous les composants que vous allez utiliser possèdent donc des noms anglais. Afin de maintenir une bonne lisibilité de votre code, vous allez respecter les règles de nommage en langue anglaise pour les noms de classes, de propriétés, de méthodes, de variables, de fonctions, de fichiers, … Ceci vaudra également pour les commentaires ainsi que les messages de commit. Les instructions et informations notables consignées dans le fichier « README.md
» pourront être en français. L'interface utilisateur de l'application sera également en français.
Documentation du projet ¶
Tout projet de développement doit fournir les clés pour l'installation, la configuration et le démarrage par un nouveau développeur. Cet effort de documentation peut également s'avérer bénéfique pour le créateur du projet qui peut oublier certains points essentiels de configuration au fil du temps. Ainsi, vous allez rédiger le mode d'emploi de votre projet dans un fichier « REAMDE.md
» et le tenir à jour au fil du développement.
Travail à réaliser
- Créez le fichier «
README.md
» à l'aide dePhpStorm
(qui vous propose de l'ajouter à l'indexGit
) - Complétez les informations suivantes :
- un titre de niveau 1 contenant le titre explicite du projet
- un titre de niveau 2 « Auteur(s) » vous permettant de préciser votre nom et votre prénom (ainsi que ceux de votre binôme, le cas échéant)
- un titre de niveau 2 « Installation / Configuration » précisant les points essentiels permettant d'installer, de configurer et de lancer le projet
- Validez votre travail dans
Git
Standards de codage PHP avec PHP CS Fixer
¶
Travail à réaliser
- Installez
PHP CS Fixer
sans oublier l'option «--dev
» - Configurez
PHP CS Fixer
pour utiliser 4 cœurs en parallèle - Configurez
PHP CS Fixer
dansPhpStorm
Qualité de code avec PHPStan
¶
Travail à réaliser
- Installez
PHPStan
sans oublier l'option «--dev
» - Configurez
PHPStan
dansPhpStorm
Standards de codage Twig avec Twig CS Fixer
¶
Travail à réaliser
- Installez
Twig CS Fixer
sans oublier l'option «--dev
»
Mise en place de scripts Composer
¶
Les dépendances de votre projet Symfony
sont gérées par Composer
. Profitons-en pour mettre en place quelques scripts Composer
qui faciliteront la gestion quotidienne du projet.
Travail à réaliser
- Ajoutez un script «
start
» qui lance le serveur web de test («symfony serve
») sans restriction de durée d'exécution - Ajoutez un script «
test:csfixer
» qui lance la commande de vérification du style de codePHP
parPHP CS Fixer
- Ajoutez un script «
fix:csfixer
» qui lance la commande de correction du style de codePHP
parPHP CS Fixer
- Ajoutez un script «
test:phpstan
» qui lance la commande de vérification du codePHP
parPHPStan
- Ajoutez un script «
test:twig
» qui lance la commande de vérification du style de codeTwig
parTwig CS Fixer
- Ajoutez un script «
fix:twig
» qui lance la commande de correction du style de codeTwig
parTwig CS Fixer
- Ajoutez un script «
test:yaml
» qui lance la commande de la consoleSymfony
permettant la vérification des fichiersYAML
contenus dans le répertoire «config
»InformationLancez la commande suivante pour découvrir les divers « linters » fournis par la console :
php bin/console lint help
- Ajoutez un script «
test
» qui lance tous les scripts de test - Ajoutez un script «
fix
» qui lance tous les scripts de correction de style de code - Documentez vos scripts dans le fichier «
composer.json
» - Documentez vos scripts dans le fichier «
README.md
»
Automatisation des contrôles de qualité de code avec GrumPHP
¶
Travail à réaliser
- Installez
GrumPHP
, sans oublier l'option «--dev
», et demandez la génération du fichier de configuration, puis ajoutez la tâche «composer
» à l'aide de l'assistant de configurationInformationLorsque
Composer
demande si vous souhaitez exécuter les scripts de post-installation, répondez «y
» :phpro/grumphp contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins Do you trust "phpro/grumphp" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?] y
- Vérifiez que le fichier de configuration ressemble à celui-ci :
grumphp: tasks: { composer: null }
- Donnez au fichier de configuration une forme plus usuelle au niveau du tableau associatif «
tasks
»grumphp: tasks: composer: null
- Tentez de valider votre travail dans
Git
Remarque importanteSelon la façon dont votre environnement
Linux
est configuré, et si vous utilisezGit
à traversPhpStorm
, il se peut que vous rencontriez l'erreur suivante lors des tâchesGrumPHP
:The executable for "composer" could not be found.
Ceci est lié aux variables d'environnement accessibles par
PhpStorm
, en particulier «$PATH
», selon que vous le lancez par le menu de votre gestionnaire de fenêtres ou en ligne de commande. Pour résoudre ce problème, référez-vous au point « Exécution deComposer
à traversPhpStorm
» du guide « Installation et configuration dePhpStorm
». - Vous constatez que
GrumPHP
empêche la validation de votre travail : la tâche «composer
» échoue- ajoutez les éléments nécessaires au fichier «
composer.json
» selon les indications deGrumPHP
- cherchez l'option de «
composer update
» permettant de mettre à jour uniquement le fichier «composer.lock
» - mettez à jour uniquement le fichier «
composer.lock
»
- ajoutez les éléments nécessaires au fichier «
- Configurez
GrumPHP
:Remarque importanteAjoutez au fichier de configuration uniquement ce qui est demandé.
- désactivez le «
fixer
» intégré (voir la documentation des paramètres deGrumPHP
) - ajoutez la tâche «
git_blacklist
» et interdisez les expressions «dump(
», «var_dump(
», «print_r(
», «die(
», «exit(
» et «exit;
» - lisez la spécification «
Commits Conventionnels
» - ajoutez la tâche «
git_commit_message
» avec les options suivantes :- le sujet du commit ne commence pas forcément par une majuscule
- les types de commit se limitent à
«
build
», «ci
», «chore
», «docs
», «feat
», «fix
», «perf
», «refactor
», «revert
», «style
» et «test
» - les portées des commits (« scopes ») sont libres
- ajoutez la tâche «
composer_script
» pour déclencher le scriptComposer
«test:yaml
» - ajoutez la tâche «
composer_script
» pour déclencher le scriptComposer
«test:twig
»InformationVous ne pouvez pas ajouter directement deux tâches
GrumPHP
portant le même nom. Vous devez donc ajouter une tâche «composer_script
» pour chaque scriptComposer
que vous souhaitez exécuter selon la méthode définie dans la documentation. - ajoutez la tâche «
phpcsfixer
»InformationN'oubliez pas l'option «
config
» de cette tâche. - ajoutez la tâche «
phpstan
»
- désactivez le «
Installation / configuration des greffons PhpStorm
¶
Vous allez maintenant installer les greffons nécessaires à la gestion de votre projet Symfony
.
Travail à réaliser
- Installez les greffons suivants :
- Activez le support de
Symfony
pour le projet - Configurez le greffon
GitHub Copilot
dont l'icône doit apparaître dans la barre d'état en bas de la fenêtre dePhpStorm
Conteneurisation des services de base de données ¶
Les projets Symfony
générés avec l'outil ligne de commande « symfony
» contiennent un fichier « compose.yaml
» et un fichier « compose.override.yaml
». Les images utilisées par défaut sont celles de PostgreSQL
dans sa variante alpine
et de Mailpit.
Nous allons dans un premier temps nous concentrer sur la base de données pour changer l'image PostgreSQL
utilisée et ajouter une image dérivée de Adminer
permettant la connexion automatique à l'interface.
Travail à réaliser
- Modifiez le fichier «
compose.yaml
» pour utiliser l'imagePostgreSQL
classique en remplacement de celle basée suralpine
- Modifiez le fichier «
compose.override.yaml
» pour :- exposer le port du service de base de données
- ajouter l'image «
michalhosna/adminer-docker
» en tant que service - configurer les variables d'environnement du service «
adminer
» pour que l'interface soit disponible sur le port «7080
» et que la connexion à la base de données soit automatiqueInformationNe touchez ni à configuration du conteneur
PostgreSQL
, ni au «DATABASE_URL
» par défaut du fichier «.env
». Modifiez uniquement les variables d'environnement du service «adminer
» pour les faire correspondre au reste de la configuration.
- Lancez vos conteneurs
- Vérifiez le bon fonctionnement de la base de données avec :
bin/console doctrine:query:sql "SELECT 1"
- Vérifiez le bon fonctionnement de l'interface
Adminer
en vous connectant à l'interface Web sur le port «7080
»
Les annonces dans l'application ¶
La base de l'application s'appuie sur les annonces d'objets à vendre. Vous mettrez en place une entité Advertisement
pour gérer ces annonces. Après avoir généré des données factices (« fixtures »), les premières fonctionnalités de l'application seront l'affichage de la liste des annonces et la consultation d'une annonce. Viendront ensuite la création, la modification et la suppression d'une annonce. Vous automatiserez la gestion des dates de création et de mise à jour de ces annonces. Les aspects de présentation seront facilités par l'utilisation de Bootstrap
.
Création d'une entité « Advertisement
»
¶
Afin de respecter le codage en langue anglaise, l'entité correspondant à une annonce sera nommée « Advertisement
».
Travail à réaliser
- Utilisez la commande du «
MakerBundle
»Symfony
pour générer l'entité «Advertisement
» avec les propriétés suivantes :- «
title
» de typestring(100)
, non null - «
description
» de typetext
, non null - «
price
» de typeint
, non null - «
createdAt
» de typedatetime_immutable
, non null - «
updatedAt
» de typedatetime_immutable
- «
location
» de typestring(100)
, non null
- «
- Créez une migration
Génération de données factices ¶
Afin de pouvoir tester votre application, vous allez proposer des données factices correspondant aux annonces. Pour cela, vous utiliserez le « DoctrineFixturesBundle
» ainsi que le paquet « Foundry
».
Travail à réaliser
- Installez le «
DoctrineFixturesBundle
» - Installez le paquet «
Foundry
» - Créez un modèle d'annonces avec «
Foundry
» - Configurez le modèle pour qu'il génère des annonces factices :
- avec un titre de maximum 100 caractères
- avec une description de maximum 500 caractères
- avec un prix entre 0 et 1500 €
- avec une localisation correspondant à une ville française
- créées entre maintenant et il y a 2 mois
- sans date de mise à jour
- Créez une classe de données factices «
AdvertisementFixtures
» - Modifiez «
AdvertisementFixtures
» pour créer 500 annonces aléatoires
Création de la base de données ¶
La création de la base de données en mode développement est une opération classique qui doit être facilement accessible. Vous devez disposer d'un script Composer
.
Travail à réaliser
- Ajoutez un script
Composer
«db
» permettant de :- forcer la suppression de la base de données, si elle existe
- créer la base de données
- appliquer les migrations
- charger les données factices
- Donnez une description de votre script dans le fichier «
composer.json
» - Documentez votre script dans le fichier «
README.md
» - Utilisez le service «
Adminer
» pour vérifier que les données factices ont bien été insérées dans la base de données
Visualisation des annonces ¶
Les annonces étant désormais créées en base de données, il est temps de les afficher. Dans un premier temps, vous proposerez une liste des annonces. Dans un second temps, vous proposerez une page de détail pour chaque annonce.
Travail à réaliser
- Utilisez la commande du «
MakerBundle
»Symfony
pour générer un contrôleur «AdvertisementController
» - Proposez la liste des annonces triées par ordre inverse de création dans l'action «
index()
»- Ajoutez une méthode «
findAllOrderedByDate()
» dans le dépôt «AdvertisementRepository
» - Utilisez la précédente méthode pour obtenir la liste des annonces et la transmettre à la vue qui se chargera de construire une liste
HTML
avec titre et date de création de chaque annonce
- Ajoutez une méthode «
- Proposez le détail d'une annonce dans l'action «
show()
»- Ajoutez une validation du paramètre de la route
- Utilisez un convertisseur d'attribut de requête pour obtenir l'annonce à partir de son identifiant
- Transmettez l'annonce à la vue qui se chargera de construire une présentation
HTML
simple des données
- Ajoutez un lien vers la page de détail de chaque annonce dans la liste des annonces
Désactivation de Symfony UX Turbo
¶
Depuis la version 7 de Symfony
, Symfony UX Turbo
est activé par défaut. Ce « bundle », faisant partie de l'initiative Symfony UX
, apporte le support de Hotwire Turbo
qui amène une expérience utilisateur de type application web monopage (SPA en anglais) sans avoir à écrire de Javascript
. Vous allez le désactiver pour éviter les problèmes de compatibilité avec Bootstrap
ainsi que des comportements de l'application, induits par la magie de Symfony UX Turbo
, qui perturberaient votre compréhension du fonctionnement de Symfony
.
Travail à réaliser
- Localisez le fichier contenant la liste des contrôleurs
Stimulus
installés - Désactivez le contrôleur
Stimulus
«@symfony/ux-turbo
»
Utilisation de Bootstrap
CDN dans l'interface utilisateur
¶
Vous allez maintenant améliorer la présentation de votre application en y incorporant Bootstrap
. Votre tâche sera facilitée par les composants HTML
et les classes CSS
proposés. Vous utiliserez ces composants pour améliorer l'interface utilisateur (UI) et l'expérience utilisateur (UX) de votre projet.
Dans un premier temps, vous intégrerez Bootstrap
à votre projet via un CDN. Par la suite, vous installerez Bootstrap
via AssetMapper
. Ceci vous permettra une intégration complète à votre projet et autorisera la personnalisation de Bootstrap
.
Travail à réaliser
- Générez un contrôleur «
HomeController
» qui permet de rediriger les requêtes de la page d'accueil vers la liste des annonces - Modifiez le « template » de base de votre projet
- Ajoutez les liens vers les fichiers
Bootstrap
- Définissez le « viewport »
- Ajoutez la langue par défaut
- Ajoutez les liens vers les fichiers
- Structurez le « template » de base de votre projet en utilisant les classes de «
Bootstrap
» :- Bloquez la largeur maximale du contenu principal
- Ajoutez une barre de navigation en haut de la page
- Donnez du style aux listes d'annonces
- Donnez du style aux annonces
- Modifiez la configuration de
Twig
pour appliquer le thèmeBootstrap
aux formulaires
Formulaire d'édition / création d'une annonce ¶
Le même formulaire doit permettre d'effectuer la saisie d'une nouvelle annonce ou l'édition d'une annonce existante.
Travail à réaliser
- Utilisez la commande du «
MakerBundle
»Symfony
pour générer une classe de formulaire «AdvertisementType
» pour l'entité «Advertisement
» - Supprimez les champs de saisie de date de création et de modification qui seront gérés automatiquement
- Précisez les types de champs de saisie restants
- Ajoutez des contraintes de validation sur l'entité «
Advertisement
» :- «
title
» est non vide et contient entre 10 et 100 caractères - «
description
» est non vide et contient entre 20 et 1000 caractères - «
price
» est positif ou nul («≥ 0
» mais pas «null
») - «
location
» est non vide et contient entre 2 et 100 caractères
- «
- A l'aide de l'option «
attr
» des champs de la classe de formulaire, précisez les attributsHTML
améliorant l'ergonomie de la saisie :- «
title
» contient entre 10 et 100 caractères - «
description
» contient entre 20 et 1000 caractères - «
price
» est positif ou nul - «
location
» contient entre 2 et 100 caractères
- «
Automatisation des dates de création et de mise à jour ¶
Un certain nombre de tâches de gestion de données se retrouvent dans de nombreux projets. C'est le cas des champs de date de création ou de mise à jour. Aussi, « StofDoctrineExtensionsBundle
», une extension de Doctrine
, propose un ensemble de fonctionnalités qui peuvent être activées pour automatiser ces tâches ainsi que d'autres dans le même esprit. La mécanique repose sur l'utilisation d'attributs PHP
8 associés aux propriétés des entités.
Dans le cas des annonces, la date de création doit être automatiquement renseignée lors de la création d'une annonce. De même, la date de mise à jour doit être automatiquement renseignée lors de la modification d'une annonce.
Travail à réaliser
- Installez l'extension «
StofDoctrineExtensionsBundle
»Remarque importanteExécutez bien la recette associée :
Symfony operations: 1 recipe (e371a4c7f75ca061c8026ed12542ac5b) - WARNING stof/doctrine-extensions-bundle (>=1.2): From github.com/symfony/recipes-contrib:main The recipe for this package comes from the "contrib" repository, which is open to community contributions. Review the recipe at https://github.com/symfony/recipes-contrib/tree/main/stof/doctrine-extensions-bundle/1.2 Do you want to execute this recipe? [y] Yes [n] No [a] Yes for all packages, only for the current installation session [p] Yes permanently, never ask again for this project (defaults to n): y
- Activez l'extension «
Timestampable
» - Utilisez les attributs
PHP
8 pour automatiser les dates de création et de mise à jour :- La date de création doit être automatiquement renseignée lors de la création d'une annonce
- La date de mise à jour doit être automatiquement renseignée lors de la modification des propriétés «
title
», «description
», «price
» ou «location
» d'une annonce
Utilisation du formulaire d'édition / création d'une annonce ¶
Le formulaire de saisie d'une nouvelle annonce ou d'édition d'une annonce existante étant créé, il convient à présent de l'utiliser. Pour cela, vous créerez deux nouvelles actions dans le contrôleur « AdvertisementController
» pour afficher le formulaire de saisie et enregistrer une nouvelle annonce ainsi que pour afficher le formulaire d'édition et modifier une annonce existante.
Travail à réaliser
- Créez un modèle «
advertisement/_form.html.twig
» qui affiche le formulaire de saisie d'une annonce et le bouton de soumission, selon les bonnes pratiques concernant les formulaires - Ajoutez une action «
new()
» (URL «/advertisement/new
») dans le contrôleur «AdvertisementController
», qui utilise «advertisement/_form.html.twig
» pour afficher le formulaire de saisie d'une nouvelle annonce - Ajoutez une action «
edit()
» (URL «/advertisement/12/edit
») dans le contrôleur «AdvertisementController
», qui utilise «advertisement/_form.html.twig
» pour afficher le formulaire d'édition d'une annonce existante - Rendez les actions fonctionnelles afin qu'elles puissent créer une nouvelle annonce ou enregistrer les modifications apportées à une annonce existante
Suppression d'une annonce ¶
L'action de suppression manque pour compléter le CRUD des annonces.
Travail à réaliser
- Ajoutez une action «
delete()
» (URL «/advertisement/12
») dans le contrôleur «AdvertisementController
», vide pour l'instant, accessible uniquement via la méthodeHTTP
POST
Remarque importanteLa suppression d'une annonce doit être réalisée en
POST
pour éviter les suppressions accidentelles. L'URL
de l'action est de la même forme que celle de l'affichage d'une annonce : «/advertisement/12
». La seule façon de distinguer les deux routes est la méthodeHTTP
utilisée. Pensez à limiter chaque route aux méthodesHTTP
qu'elle utilise. - Créez un « template » partiel pour le bouton de suppression d'une annonce Information
Le « template » partiel sera inclus dans chaque vue où un bouton de suppression d'une annonce est nécessaire. Il est constitué d'un formulaire
HTML
enPOST
contenant uniquement un bouton d'envoi. Afin d'éviter les suppressions intempestives, une confirmation réalisée enJavaScript
sera associée à l'événement de soumission du formulaire :<form … onsubmit="return confirm('Message de confirmation de suppression');">
- Implémentez la logique de suppression d'une annonce dans l'action «
delete()
» - Protégez la suppression de l'annonce des attaques « CSRF » en ajoutant un jeton « CSRF » au formulaire de suppression
- Contrôlez la présence du jeton « CSRF » dans l'action associée à la suppression
- Dans la vue des détails d'une annonce, ajoutez un bouton d'édition ainsi que le formulaire de suppression
Catégories d'annonces ¶
Les annonces seront plus faciles à trouver si elles sont rangées par catégorie.
Information
Vous veillerez à structurer correctement les informations présentées dans les vues et à fournir une interface utilisateur agréable en profitant de Bootstrap 5
.
Création de l'entité « Category
»
¶
Vous allez tout d'abord générer l'entité « Category
».
Travail à réaliser
- Utilisez la commande du «
MakerBundle
»Symfony
pour générer l'entité «Category
» avec les propriétés suivantes :- «
name
» de typestring(50)
, non null
- «
- Vérifiez le code généré
Relations entre les entités « Advertisement
» et « Category
»
¶
L'entité « Advertisement
» doit être liée à l'entité « Category
» afin de pouvoir associer une annonce à une catégorie. Une annonce appartient forcément à une catégorie et une catégorie peut contenir plusieurs annonces.
Travail à réaliser
- Utilisez la commande du «
MakerBundle
»Symfony
pour modifier l'entité «Advertisement
» :- ajoutez une relation «
category
» obligatoire vers l'entité «Category
» - ajoutez la relation inverse «
advertisements
» dans l'entité «Category
»
- ajoutez une relation «
- Créez une migration
Données factices ¶
Afin d'apporter un peu de lisibilité au jeu de données d'essai, vous allez construire la collection de catégories à partir d'un fichier contenant une liste réelle.
Travail à réaliser
- Récupérez le fichier «
category.txt
» et placez-le dans le répertoire «data
» du projet - Générez une classe de « fixtures » pour l'entité «
Category
» - Générez une «
Factory
» pour l'entité «Category
» - Importez les données du fichier dans la base de données en utilisant la «
Factory
» et la classe de « fixtures »InformationLe fichier contenant les données est localisé dans le répertoire «
data
» du projet. La classe de « fixtures », qui se trouve dans un autre répertoire, doit l'ouvrir et le lire. Il est donc nécessaire de préciser le chemin absolu du fichier. Pour cela, vous utiliserez les fonctionnalités d'« autowiring » deSymfony
, plus particulièrement le branchement manuel de paramètre pour accéder à la valeur du chemin du projet et contruire le chemin vers le fichier d'intérêt. - Modifiez la «
Factory
» de l'entité «Advertisement
» pour qu'elle associe chaque nouvelle annonce à une nouvelle catégorie - Rétablissez l'ordre logique de génération des « fixtures » pour que les catégories soient créées avant les annonces
- Vérifiez le bon chargement des données avec le service
Docker
Adminer
Données factices, introduction de « Story
»
¶
Les jeux de données d'essai peuvent devenir complexes à construire et sont alors difficilement réutilisables. Foundry
propose le concept de « Story
» qui permet de définir un ensemble de données cohérentes et réutilisables.
Travail à réaliser
- Lisez le chapitre de la documentation de
Foundry
consacré aux «Stories
» - Générez une «
Story
» pour l'entité «Category
» - Importez les données du fichier «
category.txt
» dans un tableauPHP
- Extrayez la première catégorie de votre import pour créer un «
State
» que vous nommerez «category_without_advertisement
» - Utilisez le reste du tableau de noms de catégories pour construire un «
Pool
» que vous nommerez «categories
» - Modifiez la classe de « fixtures » associée à l'entité «
Category
» afin qu'elle utilise la «Story
» pour générer toutes les catégories - Modifiez la classe de « fixtures » associée à l'entité «
Advertisement
» afin qu'elle utilise la «Story
» pour générer des annonces dont la catégorie sera tirée au hasard dans le «Pool
» «categories
» - Améliorez les performances de la génération du jeu d'essai en utilisant la fonction «
flush_after()
» pour toutes les « fixtures » des catégoriesInformationDepuis la version 2 de
Foundry
, l'utilisation deflush_after()
conjointement avec desPool
issus desStory
conduit à un comportement anormal.
Liste des annonces par catégorie ¶
Les utilisateurs doivent pouvoir visualiser les annonces par catégorie. Vous allez donc proposer la liste des catégories ainsi que la liste des annonces pour chaque catégorie.
Travail à réaliser
- Utilisez la commande du «
MakerBundle
»Symfony
pour générer un contrôleur «CategoryController
» - Utilisez la route par défaut du contrôleur pour afficher la liste des catégories triées par ordre alphabétique
- Définissez une route «
show
» pour afficher la liste des annonces d'une catégorieInformationPuisque vous avez déjà proposé une vue d'une liste d'annonces, il serait judicieux de mettre le code en commun dans un fragment de « template » qui sera nommé selon les bonnes pratiques. Ce dernier sera inclus ou intégré selon votre choix, et comprendra le titre «
h1
» qui pourra être personnalisé. - Ajoutez un lien sur chaque catégorie de la liste pour afficher les annonces de la catégorie
Affichage de la catégorie d'une annonce ¶
L'ergonomie de l'interface utilisateur sera améliorée si chaque annonce affiche la catégorie à laquelle elle est associée, aussi bien dans la vue détaillée de l'annonce que dans les listes d'annonces.
Travail à réaliser
- Ajoutez le nom de la catégorie dans la vue détaillée d'une annonce
- Ajoutez le nom de la catégorie dans la liste des annonces
- Ajoutez un lien sur la catégorie pour afficher les annonces de la catégorie
- Ajoutez une entrée dans la barre de navigation pour afficher la liste des catégories
Formulaire de saisie d'une annonce ¶
L'annonce possède une relation vers la catégorie. Il convient donc de proposer un champ de saisie permettant de sélectionner la catégorie de l'annonce.
Travail à réaliser
- Lisez la documentation de
Symfony
sur les champs de formulaires associés aux entités - Ajoutez un champ de saisie de catégorie dans le formulaire de saisie d'une annonce
- Affichez le nouveau champ de saisie dans la vue
- Dans la vue, pensez à ajouter une entrée dans la liste déroulante pour forcer le choix d'une catégorie
Considérations sur les performances ¶
L'ORM Doctrine
facilite la tâche du développeur en proposant une interface PHP
pour la gestion des données. Cependant, cette interface a un coût en termes de performance et il est nécessaire d'analyser les requêtes pour voir s'il est possible d'optimiser les performances de l'application. Dans le cas présent, la relation entre les annonces et les catégories implique d'effectuer, sur une collection d'annonces, une nouvelle requête en base de données pour récupérer la catégorie de chacune des annonces.
Catégorie de l'annonce dans la liste des annonces ¶
Vous allez analyser puis optimiser le requêtage des annonces au sein des listes.
Travail à réaliser
- Affichez la liste des annonces dans votre navigateur
- Observez les requêtes effectuées dans la partie
Doctrine
du «Profiler
» accessible par la barre de débogage (« Web Debug Toolbar ») - Expliquez pourquoi une requête est effectuée pour chaque catégorie
- Proposez une solution pour optimiser le requêtage au niveau du «
Repository
»InformationLe nom de la méthode du «
Repository
» doit refléter votre démarche d'optimisation. Vous devez donc le compléter avec un suffixe qui indique les entités supplémentaires sélectionnées. - Vérifiez que votre solution diminue le nombre de requêtes effectuées
Taille de la liste des annonces ¶
Du point de vue des performances, mais également de l'expérience utilisateur, les listes de grande taille doivent être paginées. C'est une problématique courante, dont la solution peut être trouvée dans un « bundle ». Concrètement, d'un point de vue base de données, il s'agit de limiter le nombre de résultats retournés par une requête. Cependant, il est nécessaire de connaître le nombre total de résultats pour pouvoir proposer une pagination. C'est pourquoi, il est nécessaire d'effectuer deux requêtes : une pour récupérer les résultats et une pour récupérer le nombre total de résultats.
Travail à réaliser
- Lisez les explications de la limitation de sélection de résultats dans la documentation « PHP MySQL Limit Data Selections » de
W3Schools
- Parcourez rapidement la documentation de «
KnpPaginatorBundle
» - Installez «
KnpPaginatorBundle
» dans votre projet - Utilisez «
KnpPaginatorBundle
» pour paginer la liste de toutes les annoncesInformationPour réaliser la pagination, vous devez utiliser un paramètre d'
URL
pour désigner la page demandée. La méthode classique pour récupérer ce paramètre est d'ajouter l'objetRequest
en argument de la méthode du contrôleur. Il est désormais possible d'utiliser le mappage automatique de la requêteHTTP
en utilisant l'attributPHP
«#[MapQueryParameter]
». Ceci offre une meilleure lisibilité du code et permet également d'appliquer des filtres de validation. - Modifiez le fichier de configuration de «
KnpPaginatorBundle
» pour appliquer le thèmeBootstrap 5
à la paginationInformationDans la documentation, le nom et le chemin du fichier de configuration
YAML
ne sont pas indiqués. Les fichiers de configuration des packages installés dans le projet se trouvent dans «config/packages
». A moins que ce soit un package du frameworkSymfony
, le fichier de configuration est nommé de la même manière que la première clé du fichierYAML
suivie de l'extension «.yaml
».Remarque importanteSi le thème
Bootstrap 5
ne s'applique pas, effacez le cache de l'application. - Observez la probable absence de répercussions sur les requêtes effectuées (absence de «
LIMIT
» dans les requêtes)Information«
KnpPaginatorBundle
» permet de paginer de nombreuses formes de collections. Si vous appliquez la pagination à la liste des annonces sous forme de résultat de requête, vous paginez un tableau de toutes les annonces. «KnpPaginatorBundle
» doit avoir accès à la requête pour pouvoir la modifier. C'est pourquoi, il est nécessaire de modifier votre approche en paginant une requête et non une collection issue d'un résultat de requête. - Modifiez le code de la méthode pour qu'elle retourne la requête et non son résultat
- Modifiez le nom de la méthode «
findAllOrderedByDate…()
» du «Repository
» en «queryAllOrderedByDate…()
» pour refléter le changement d'approche - Vérifiez que la pagination fonctionne correctement et que les requêtes sont optimisées (présence de «
LIMIT
») - Procédez de la même manière pour la liste des annonces d'une catégorie
- Modifiez la valeur par défaut du nombre d'annonces par page pour afficher 15 annonces par page
Pagination de la liste des catégories ¶
Afin d'uniformiser l'application, vous allez paginer la liste des catégories.
Travail à réaliser
- Modifiez tout le code nécessaire pour paginer la liste des catégories
- Vérifiez que la pagination fonctionne correctement et que les requêtes sont optimisées (présence de «
LIMIT
») - Vérifiez que la valeur par défaut du nombre de catégories par page est correcte
Catégorie dans les détails de l'annonce ¶
La récupération de l'annonce lors de son affichage détaillé peut bénéficier d'une requête optimisée. En effet, la relation entre l'annonce et la catégorie est connue lors de la récupération de l'annonce. Il est donc possible de récupérer la catégorie en même temps que l'annonce, en utilisant une jointure.
Travail à réaliser
- Lisez la documentation « Automatically Fetching Objects (EntityValueResolver) », en particulier le point sur l'utilisation d'une expression avec l'attribut
PHP
«MapEntity
» - Définissez une méthode dans le «
Repository
» pour répondre à la problématique - Associez la méthode du «
Repository
» à l'action «show()
» à l'aide de l'attributPHP
«MapEntity
» - Vérifiez les requêtes effectuées lors de l'affichage détaillé d'une annonce
- Associez la méthode du «
Repository
» à l'action «edit()
» à l'aide de l'attributPHP
«MapEntity
» - Vérifiez les requêtes effectuées lors de l'édition d'une annonce
Mise en place de tests ¶
Un projet informatique qui inclut des tests présente de nombreux avantages, directement liés à la détection précoce des problèmes :
- amélioration de la qualité du code
- non régression lors des évolutions
- possibilité de développement itératif par intégration continue et déploiement continu
- facilitation de la collaboration entre développeurs en contenant les effets de bord des modifications de chacun
- fiabilisation des mises à jour des composants (serveur, langage de programmation, « frameworks », « toolkits », …)
Les projets Web demandent un effort particulier sur les tests fonctionnels ou d'acceptation. Symfony s'appuie sur PHPUnit
pour proposer des tests unitaires, d'intégration et fonctionnels. Les tests fonctionnels (« Application Tests ») sont particulièrement adaptés aux projets Web, car ils permettent de tester l'application dans son ensemble, en simulant un navigateur qui va émettre une requête HTTP
vers l'application pour provoquer une réponse qui sera récupérée, analysée et testée.
Sur le même principe, Codeception
propose un module Symfony
. Nous utiliserons Codeception pour la lisibilité apportée par la syntaxe de ses tests.
Installation/configuration de Codeception
¶
Vous allez dans un premier temps installer Codeception
et les modules nécessaires.
Travail à réaliser
- Installez
Codeception
, sans oublier que c'est une dépendance de développementInformationAjoutez l'option «
--no-interaction
» pour éviter d'avoir à répondre à des questions et surtout pour empêcher la génération des suites de tests par défaut. - Installez en une seule commande les modules «
Asserts
», «Symfony
» et «Doctrine
» deCodeception
- Constatez l'échec de l'installation Information
La lecture attentive des messages d'erreur vous indique qu'il faut mettre à jour
PHPUnit
vers la version 10. - Modifiez la version de
PHPUnit
dans le fichier «composer.json
» pour qu'elle soit compatible avecCodeception
, en spécifiant une sous-version 10.5 - Lancez la mise à jour de
PHPUnit
avecComposer
composer update phpunit/phpunit
- Constatez l'échec de la mise à jour qui demande également de mettre à jour «
phpunit/php-code-coverage
» - Lancez à présent la mise à jour de
PHPUnit
ainsi que de tous les paquets dépendants avec l'option «-w
» ou «--with-dependencies
»composer update -w phpunit/phpunit
- Constatez la réussite de la mise à jour de
PHPUnit
, ainsi que d'une vingtaine de paquets - Relancez l'installation des modules de
Codeception
- Lisez le guide de démarrage rapide de
Codeception
- Consultez les options d'amorçage (« bootstrap ») de
Codeception
- Amorcez
Codeception
dans votre projet en précisant le namespace «App\Tests
» et en ne générant pas les suites par défautInformationEn ligne de commande, si vous souhaitez passer un paramètre contenant le caractère «
\
», vous devez le doubler puisque le shell utilise ce même caractère «\
» pour échapper les caractères. Ainsi, le namespace «App\Tests
» devient «App\\Tests
» dans les paramètres de la ligne de commande. - Chargez les paramètres de configuration dans la configuration générale de
Codeception
depuis les fichiers «.env
» et «.env.test
» - Configurez l'environnement de test de
Symfony
pour qu'il utilise une base de données «SQLite
» (trouvez le «DATABASE_URL
» adéquat dans le fichier «.env
») - Excluez les sources générées par
Codeception
(«tests/Support/_generated
») de l'analyse de code dePHP CS Fixer
- Excluez les sources générées par
Codeception
(«tests/Support/_generated
») de l'analyse de code dePHPStan
InformationCodeception
fonctionne en partie en générant du codePHP
dans le répertoire «tests/Support/_generated
». Ce code est nécessaire au fonctionnement deCodeception
, ainsi qu'à l'analyse statique de code effectuée parPHPStan
, manuellement ou à traversGrumPHP
. Vous serez donc peut-être amené à forcer la génération pour pouvoir effectuer un « commit ». Il peut également être nécessaire d'effacer le cache de résultats dePHPStan
pour que certaines modifications soient prises en compte. - Créez un script
Composer
nommé «test:codeception
» qui :- nettoie les fichiers générés par
Codeception
- détruit la base de données dans l'environnement de test
- crée la base de données dans l'environnement de test
- crée le schéma de la base de données dans l'environnement de test
- exécute les tests de
Codeception
InformationLe nom du script
Composer
«test:codeception
» peut sembler très long. Cependant, il est important de le nommer de manière explicite pour que chacun comprenne immédiatement son rôle. De plus, il est possible de raccourcir ce nom à l'utilisation en utilisant la commandeComposer
tant qu'il n'y a pas d'ambiguïté avec d'autres scripts. Ansi, la commandeComposer
permettant d'exécuter ce script peut être réduite àcomposer test:co
Ceci est naturellement valable pour tous les scriptsComposer
que vous créez, ainsi que les commandes classiques comme «install
». - nettoie les fichiers générés par
- Décrivez ce script dans «
composer.json
» et dans la documentation de votre projet - Complétez le script
Composer
nommé «test
» afin qu'il exécute les testsCodeception
à la suite des scripts de tests que vous avez créés jusqu'à présent
Quelques ajustements des outils de qualité de code ¶
La configuration des outils de qualité de code mis en place précédemment peut être affinée. PHPStan
sera plus adapté par l'intégration des extensions « PHPStan Extension Installer
» puis « PHPStan Symfony Framework extensions and rules
» et « Doctrine extensions for PHPStan
». Dans un souci d'organisation du projet, les fichiers de cache peuvent être relocalisés dans le répertoire « var
» de l'application. Pour finir, certains script Composer
peuvent être modifiés pour améliorer la lisibilité ou la robustesse des tests.
Travail à réaliser
- Installez l'extension «
PHPStan Extension Installer
» dePHPStan
, sans oublier d'exécuter les recettes de configuration - Installez les extensions de
PHPStan
pourSymfony
etDoctrine
- Modifiez la configuration des outils de qualité de code pour que le cache soit dans le répertoire «
var
» de l'application :- fichier de cache de
PHP CS Fixer
- fichier de cache de
Twig CS Fixer
- répertoire temporaire de
PHPStan
- répertoire de cache de
PHPUnit
- fichier de cache de
- Modifiez le script
Composer
«test:csfixer
» pour masquer la barre de progression dePHP CS Fixer
- Modifiez le script
Composer
«test:phpstan
» pour générer les classesActor
deCodeception
avant d'exécuterPHPStan
Suite de tests de « Application
»
¶
Votre première suite de tests regroupera tous les tests fonctionnels de l'application. Vous utiliserez ensuite des espaces de noms pour organiser les diverses catégories de tests.
Travail à réaliser
- Générez une suite de tests «
Application
» - Activez et configurez les modules «
Asserts
», «Symfony
» et «Doctrine
» pour la suite de tests «Application
»
« Cest » pour la liste des annonces ¶
Vos premières réalisations ont concerné la liste des annonces. Ce sera donc la première fonctionnalité que vous testerez
Travail à réaliser
- Générez un « Cest » «
Advertisement\List
» qui regroupera les tests fonctionnels de la liste des annonces - Si ce n'est pas encore fait, et afin d'écrire des tests valides pour des visiteurs français, configurez la traduction de
Symfony
pour la langue française - En utilisant vos
Factory
pour générer les données factices, effectuez les tests suivants, et corrigez votre code si nécessaire :- La liste des annonces est correctement affichée si elle est vide
- Une liste de 20 annonces s'affiche correctement, pagination comprise
« Cest » pour le CRUD des annonces ¶
Les annonces peuvent être créées, visualisées, modifiées et effacées (« CRUD »). Ces actions doivent naturellement être présentes et testées.
Travail à réaliser
- Générez un « Cest » «
Advertisement\CRUD
» qui regroupera les tests fonctionnels concernant les annonces - En utilisant vos «
Factory
» pour générer les données factices, effectuez les tests suivants, et corrigez ou complétez votre code si nécessaire :- La création d'une annonce à partir du formulaire de création fonctionne correctement et implique donc la présence des données en base de données
- L'affichage d'une annonce comporte bien ses caractéristiques
- La modification d'une annonce à partir du formulaire d'édition fonctionne correctement et implique donc la mise à jour des données en base de données
- la suppression d'une annonce fonctionne correctement et implique donc la suppression des données en base de données
InformationLes object créés par
Foundry
sont des instances de «PersistentProxyObjectFactory
» qui se comportent comme vos entités. Si vous désirez accéder à l'objet réel correspondant à votre entité, vous devez utiliser la méthode «_real()
».
« Smoke testing », test de fumée ¶
L'objectif des tests est de détecter précocement les problèmes de l'application. Le premier niveau de vérification consiste donc à vérifier que les URL de l'application répondent correctement. Chaque URL sera alors contrôlée a minima, en attendant que des tests détaillés soient écrits pour vérifier tous les aspects de la fonctionnalité implémentée. C'est ce que l'on appelle le « smoke testing ». Ces tests devront être exécutés avant tous les autres tests, ce qui va demander une utilisation particulière de Codeception
faisant appel aux groupes.
Travail à réaliser
- Générez un « Cest » «
Availability
» qui regroupera les tests de fumée - Créez un test «
pageIsAvailable
» qui vérifie que l'URL passée en paramètre est disponibleInformationRéfléchissez aux « fixtures » dans ce contexte.
- Créez un «
dataProvider
» qui fournit les URL à tester - Associez le «
dataProvider
» au test «pageIsAvailable
» - Exécutez le test «
pageIsAvailable
» pour toutes les URL connues de l'application - Lisez la documentation de
Codeception
sur les groupes - Créez un groupe «
available
» qui regroupe tous les tests de fumée - Lisez l'auto-documentation de la commande «
run
» de «vendor/bin/codecept
» - Modifiez le script
Composer
«test:codeception
» pour qu'il exécute, dans l'ordre :- les tests de fumée (ceux du groupe «
available
») - les tests de l'application (tous sauf ceux du groupe «
available
»)
- les tests de fumée (ceux du groupe «
Intégration continue ¶
L'intégration continue demande l'automatisation des tests pour détecter au plus tôt les erreurs afin d'éviter les régressions. Vous avez déjà mis en place des outils de qualité de code automatisés sur le poste de travail du développeur avec PHP CS Fixer
, PHPStan
, Twig CS Fixer
et GrumPHP
. Vous disposez également d'une batterie de tests fonctionnels. Il est temps de les automatiser dans le processus d'intégration continue.
Vous allez installer et configurer GitLab Runner
sur une machine virtuelle Ubuntu
dans la ferme OpenNebula
du département informatique. Le « runner » sera ensuite associé à votre dépôt GitLab
pour que les tests soient exécutés automatiquement à chaque « push
».
Configuration du « runner » dans une machine virtuelle ¶
Vous allez tout d'abord déployer une machine virtuelle Ubuntu
dans la ferme OpenNebula
du département informatique. La machine virtuelle Ubuntu
sera ensuite configurée pour pouvoir exécuter les tests de l'application. Elle doit donc disposer de PHP
, de Composer
et de Node.js
. Vous allez ensuite installer et configurer un « GitLab Runner
» pour exécuter les tests à chaque « push
».
Travail à réaliser
- Consultez le support de cours de Floran Brutel et Nicolas Hart sur l'intégration continue
- Suivez le guide de déploiement d'une machine virtuelle
Ubuntu
surOpenNebula
avec les paramètres suivants :- « template » «
Ubuntu Minimal 22.04
» 1024MB
de mémoire vive10GB
de disque dur
- « template » «
- Connectez-vous en
ssh
à la machine virtuelle et vérifiez qu'elle fonctionne correctement - Vérifiez que vous avez bien suivi les consignes en affichant la version du système d'exploitation :
lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy
- Effectuez les actions suivantes permettant de contourner de possibles désagréments ayant été constatés :
- Ajoutez «
localhost.localdomain
» dans «/etc/hosts
» en passant la commande :sed -i "s:^127.0.0.1 localhost$:127.0.0.1 localhost.localdomain localhost:" /etc/hosts
- Mettez à jour les dépôts :
apt update
- Ajoutez un éditeur de texte, «
vim
» ou «nano
» (Ubuntu Minimal
ne contient pas d'éditeur de texte) :apt install nano
- Ajoutez les outils de gestion des dépôts «
software-properties-common
» :apt install software-properties-common
- Limitez l'attente de la configuration réseau à 5 secondes en éditant le service «
systemd-networkd-wait-online.service
» avec la commande :systemctl edit --full systemd-networkd-wait-online.service
puis en ajoutant l'option «--timeout=5
» à la fin de la ligne commençant par «ExecStart=
»InformationMettez bien un espace entre la fin de la commande et l'option que vous ajoutez.
- Mettez à jour le système :
apt upgrade
InformationLors de la mise à jour du système, vous serez invité à confirmer la mise à jour du fichier «
/etc/ssh/sshd_config
». Conservez le fichier installé.Lors des installations à vernir, vous serez invité à redémarrer les services. Il n'est pas utile de le faire, vous redémarrerez la machine virtuelle à la fin de la configuration.
- Ajoutez «
- Ajoutez le dépôt des versions supportées de
PHP
«ppa:ondrej/php
» - Installez «
php8.3-cli
» avec les extensions «sqlite3
», «curl
», «intl
», «mbstring
», «xml
» et «zip
» - Ajoutez le dépôt officiel de
Gitlab Runner
- Installez «
gitlab-runner
» - Installez
Composer
dans «/usr/local/bin
» - Créez un « runner »
Linux
pour votre dépôtGitLab
avec les « tags » «php8.3
» et «composer
» - Enregistrez le « runner » avec la commande proposée et fournissez interactivement les informations nécessaires :
- le jeton d'enregistrement de votre dépôt
GitLab
(déjà renseigné via les options de la commande) - l'URL du serveur
GitLab
(déjà pré-renseigné via les options de la commande) - le nom du « runner » : «
symfony-for-sale
» - l'« executor » du « runner » : «
shell
»
- le jeton d'enregistrement de votre dépôt
- Désactivez les « runners d'instance » (« instance runners » en anglais) de votre dépôt
GitLab
- Redémarrez la machine virtuelle et vérifiez que le « runner » est bien actif dans votre dépôt
GitLab
Intégration continue dans le projet ¶
Vous allez pouvoir définir les tâches d'intégration continue pour votre projet. Afin de limiter le temps d'exécution et les ressources OpenNebula
consommées, vous définirez une seule tâche d'intégration continue qui installera les composants nécessaires au projet et exécutera les tests.
Travail à réaliser
- Créez le fichier «
.gitlab-ci.yml
» à la racine du projet - Définissez une tâche «
test
» qui :- nécessite les « tags » «
php8.3
» et «composer
» - installe les dépendances du projet
- exécute les tests
- conserve les artefacts des échecs des tests de
Codeception
InformationVous pouvez peaufiner la définition des artéfacts de
Codeception
en excluant le fichier «.gitignore
» contenu dans le répertoire concerné.
- nécessite les « tags » «
- Poussez le fichier «
.gitlab-ci.yml
» dans votre dépôtGitLab
- Vérifiez que le « pipepline » d'intégration continue est bien exécuté et se termine avec succès
Amélioration de l'interface et de l'expérience utilisateur ¶
L'accueil de l'application affiche toujours la page de test de Symfony
dans l'environnement de développement et une erreur HTTP
404 en production. Il faut donc effectuer une redirection vers la liste des annonces. Il manque également une fonctionnalité de rechercher des annonces.
Redirection depuis l'accueil de l'application ¶
Vous allez simplement rediriger le visiteur vers la liste des annonces depuis l'accueil de l'application.
Travail à réaliser
- Générez un nouveau contrôleur «
HomeController
» - Dans l'action «
index
» de ce contrôleur, redirigez le visiteur vers la liste des annonces - Supprimez la vue inutile associée à l'action «
index
» - Testez la fonctionnalité dans «
Application\HomeCest
»
Recherche des annonces par titre ou description ¶
Il est temps d'ajouter une barre de recherche.
Travail à réaliser
- Ajoutez un formulaire comportant un champ de recherche dans la barre de navigation
- Définissez la route correspondant à la liste des annonces comme action associée à la recherche
- Modifiez la methode de «
AdvertisementRepository
» afin qu'elle :- admette un paramètre correspondant à la chaîne de recherche
- effectue une recherche insensible à la casse sur le titre et la description des annonces Information
Inutile de complexifier la requête si la chaîne de recherche est vide.
- retourne la requête des annonces correspondant à la recherche
- Récupérez la valeur de la chaîne de recherche dans l'action affichant la liste des annonces
- Donnez cette valeur à la méthode de recherche de «
AdvertisementRepository
» - Reportez la valeur de la chaîne de recherche dans le champ de recherche du formulaire
- Ajustez le titre de la page («
title
» et «h1
») en fonction de la présence ou non d'une chaîne de recherche - Testez la fonctionnalité dans «
Application\Advertisement\SearchCest
»
Création d'une extension Twig
pour l'affichage des dates
¶
Ceci est une question complémentaire, qui n'est pas obligatoire. Les détails sont donnés au sein de l'ensemble des questions complémentaires.
Interface utilisateur ¶
Jusqu'à présent, vous avez utilisé Bootstrap
à travers un CDN. La nouveauté par rapport à l'an passé va résider dans une intégration locale de Bootstrap
, afin de ne pas dépendre d'un CDN. Ce sera également l'occasion de découvrir le composant « AssetMapper
» qui va faciliter l'intégration d'« assets » JavaScript
et CSS
dans votre application. Vous aurez également le loisir de découvrir Sass
qui va vous permettre de gérer les feuilles de style de manière plus efficace, structurée et flexible. Vous pourrez notamment personnaliser Bootstrap
en utilisant Sass
.
Installation de « AssetMapper
»
¶
Votre projet Symfony
créé avec l'option « --webapp
» comporte déjà le composant « AssetMapper
».
Travail à réaliser
- Parcourez la documentation de «
AssetMapper
» - Supprimez les liens
CSS
et leJS
parmettant l'intégration deBootstrap
dans «base.html.twig
» - Vérifiez que l'interface utilsateuir de votre application n'intègre plus
Bootstrap
- Importez
Bootstrap
à l'aide de la commande «importmap
» - Dans «
app.js
» :- Supprimez le code
JavaScript
d'exemple - Importez tout le
JavaScript
deBootstrap
- importez le
CSS
deBootstrap
- Supprimez le code
- Puisque l'application est en mode développement, les « assets » sont servis dynamiquement par l'application
Installation de Sass
¶
Le langage de script Sass
doit être interprété afin de générer les feuilles de style CSS
de votre projet. Il existe un bundle Symfony
qui permet d'interpréter les fichiers Sass
.
Travail à réaliser
- Installez « Sass For Symfony! »
- Renommez le fichier de style de l'application en «
app.scss
» - Compilez le tout nouveau fichier
Sass
avec :php bin/console sass:build
InformationPensez à ajouter la compilation de
Sass
dans la construction du projet pour l'intégration continue.
Installation de Bootstrap Sass
¶
L'avantage des CDN est de faciliter la mise en cache des bibliothèques JavaScript
et CSS
par les navigateurs. Cependant, il est préférable de ne pas dépendre d'un CDN pour des raisons de performance et de sécurité. L'intérêt majeur de ne pas utiliser de CDN pour Bootstrap
réside dans les possibilités de personnalisation de la copie locale. Vous allez donc installer « Bootstrap
» localement.
Travail à réaliser
- Installez
Bootstrap Sass
pourSymfony
- Supprimez l'import du
CSS
deBootstrap
dans «app.js
» - Importez
Bootstrap
dans le fichier «app.scss
» - Compilez le fichier
Sass
et vérifiez que votre application s'affiche correctement - Afin de faciliter le développement, écrivez un script
Composer
«sass
» qui lance la compilation dynamique des fichiersSass
:php bin/console sass:build --watch
Personnalisation du thème de couleurs de Bootstrap
¶
Les couleurs et l'aspect de Bootstrap
sont trop classiques et trop reconnaissables. Il est donc nécessaire de personnaliser le thème afin de ne pas avoir un site qui ressemble à tous les autres.
Travail à réaliser
- Choisissez un thème
Boostrap
qui vous convient sur https://huemint.com/bootstrap-plus/ - Appliquez le thème à votre projet Information
Attention à ne pas utiliser directement le code fourni par https://huemint.com/bootstrap-plus/, vous devez respecter les consignes de la documentation de
Bootstrap
pour personnaliser le thème.Mettez en commentaires les couleurs «
accent 1
», «accent 2
» et «accent 3
» qui ne font par partie du thème, mais que nous utiliserons plus tard.
Ajout de couleurs de thème dans Bootstrap
¶
Bootstrap
est conçu autour d'un thème de couleurs symboliques (« primary
», « secondary
», « success
», …) qui sont accessibles à travers de multiples sélecteurs CSS
. Par exemple, la couleur « primary
» se retrouve dans les sélecteurs « .btn-primary
», « .bg-primary
», « .text-primary
», … Il est possible d'ajouter des nouvelles couleurs symboliques afin d'augmenter la palette de couleurs disponibles. Il ne faut toutefois pas en abuser, car chaque couleur symbolique ajoutée augmente la taille et la complexité des feuilles de style générées.
Travail à réaliser
- Lisez la documentation de l'ajout de couleurs de thème
Bootstrap
- Ajoutez une nouvelle couleur symbolique «
accent1
» dans le thème deBootstrap
InformationUtilisez une des trois couleurs supplémentaires générées par https://huemint.com/bootstrap-plus/.
- Utilisez un sélecteur associé à la nouvelle couleur symbolique pour modifier la couleur du texte de l'élément «
navbar-brand
» de la barre de navigation
Personnalisation d'éléments de mise en forme de Bootstrap
¶
Vous utilisez les classes CSS
de Bootstrap
pour mettre en forme votre application. Vous pouvez également les utiliser pour les modifier.
Travail à réaliser
- Ajoutez un sélecteur
CSS
pour modifier les éléments de listes afin d'alterner la couleur de fond des lignesInformationVous pouvez utiliser la variante subtile («
bg-*-subtle
») d'une des couleurs du thème. - Changez la police des badges pour qu'elle soit plus grosse («
1rem
») et pas en caractères gras
Optimisation de Bootstrap
¶
La copie locale de Bootstrap
est trop volumineuse. Vous allez donc optimiser le chargement des fichiers JavaScript
et Sass
en n'incluant que les éléments réellement utilisés.
Travail à réaliser
- Lisez la partie optimisation de la documentation de la personnalisation de
Bootstrap
- Optimisez les imports de fichiers
JavaScript
en n'incluant que les éléments réellement utilisés - Optimisez les imports de fichiers
Sass
en n'incluant que les éléments réellement utilisésInformationIl n'est pas toujours simple de trouver les fichiers nécessaires, mais il est primordial de respecter l'ordre d'inclusion défini dans le fichier «
scss/bootstrap.scss
» deBootstrap
. Partez du contenu de ce fichier et commentez les imports inutiles.
Bootstrap Icons
¶
Les bibliothèques d'icônes sont largement utilisées dans les applications Web. Vous allez intégrer celle de Bootstrap
.
Travail à réaliser
- Lisez la documentation des
Bootstrap Icons
- Installez les
Bootstrap Icons
dans votre projet avecAssetMapper
(«bootstrap-icons/font/bootstrap-icons.min.css
») - Remplacez le texte du bouton de recherche par une icône
Bootstrap
Gestion du mode clair/sombre dans Bootstrap
¶
Ceci est une question complémentaire, qui n'est pas obligatoire. Les détails sont donnés au sein de l'ensemble des questions complémentaires.
Sécurité ¶
Jusqu'ici, l'application donne accès à toutes les fonctionnalités à toute personne y ayant accès. Naturellement, il faut restreindre l'accès à certaines fonctionnalités. Vous allez donc mettre en place un système d'authentification, des restrictions d'accès et du cloisonnement de données.
Authentification ¶
Utilisateur ¶
La première étape de sécurisation de l'application réside dans la création des utilisateurs.
Travail à réaliser
- Générez une entité utilisateur «
User
» avec les propriétés suivantes :- «
email
» de typestring(180)
, unique, not null - «
password
» - «
firstname
» de typestring(100)
, not null - «
lastname
» de typestring(150)
, not null
- «
- Ajoutez des contraintes de validation (taille maximale, type, non vide…) aux propriétés «
email
», «firstname
» et «lastname
» de l'entité «User
» - Utilisez les ancres et références de
YAML
pour reproduire la configuration de réduction de coût de hachage des mots de passe dans les environnements de test et de développement dans le fichier «config/packages/security.yaml
»InformationVous pouvez vous inspirer du fichier «
config/packages/zenstruck_foundry.yaml
» pour la syntaxe. - Créez, à l'aide d'une «
Story
», des utilisateurs factices dont le mot de passe sera «test
» :- «
admin@example.com
», avec un rôle administrateur - «
admin2@example.com
», avec un rôle administrateur - «
user@example.com
», avec un rôle utilisateur - «
user2@example.com
», avec un rôle utilisateur - 10 utilisateurs aléatoires
InformationAfin d'identifier facilement les utilisateurs non aléatoires dans votre application, définissez leurs nom et prénom dans les données factices.
Pensez à hacher les mots de passe des utilisateurs factices.
- «
- Documentez les informations des utilisateurs factices
Connexion ¶
Les utilisateurs doivent pouvoir se connecter pour s'identifier et se déconnecter.
Travail à réaliser
- Créez un formulaire de connexion en français et vérifiez manuellement son fonctionnement (succès/échec)
- Ajoutez un bouton de connexion et un bouton de déconnexion côte à côte dans la barre de navigation
- Dans un nouveau « Cest » «
Application\Security\AuthenticationCest
», testez :- qu'un utilisateur peut se connecter
- qu'un utilisateur connecté peut se déconnecter
- qu'un test de connexion avec des identifiants erronés échoue
- Complétez les tests de fumée avec les URL de connexion et de déconnexion
Autorisations ¶
Les utilisateurs maintenant identifiés peuvent être autorisés ou non à accéder à certaines fonctionnalités.
Travail à réaliser
- Affichez ou cachez les boutons de connexion/déconnexion de la barre de navigation en fonction de l'état de connexion de l'utilisateur
- Limitez l'accès à la page de création d'une annonce aux utilisateurs connectés
- Masquez le lien de création d'une annonce dans la barre de navigation aux utilisateurs non connectés
- Modifiez le test de création d'une annonce pour qu'il soit exécuté par un utilisateur connecté
- Ajoutez un test qui vérifie que la création d'une annonce par un utilisateur non connecté échoue
Cloisonnement de données ¶
Les annonces, actuellement anonymes, vont être associées à un utilisateur et les autorisations vont être ajustées en conséquence.
Association d'une annonce à un utilisateur ¶
Pour commencer, chaque annonce doit être associée à un utilisateur.
Travail à réaliser
- Ajoutez une propriété «
owner
» à l'entité «Advertisement
» permettant d'associer un seul utilisateur à chaque annonce - Affectez automatiquement l'utilisateur connecté à la création d'une annonce en utilisant «
Blameable
» de «StofDoctrineExtensionsBundle
» - Modifiez les « fixtures » pour que les 500 annonces soient créées par les 10 utilisateurs aléatoires de la «
Story
» - Ajoutez 20 annonces pour l'utilisateur «
user@example.com
» de la «Story
» - Affichez le prénom de l'utilisateur propriétaire dans les détails de l'annonce sous forme d'un badge
Bootstrap
de couleur «accent1
» - Testez qu'une annonce nouvellement créée est bien associée à l'utilisateur connecté Information
L'utilisation de «
Blameable
» en conjonction avecCodeception
demande la persistance du service «BlameListener
» pour fonctionner correctement. Cet ajustement vous est fourni sous forme d'un «Helper
»Codeception
que vous devez activer dans la suite «Application
». Vous en profiterez pour ajouter le «Helper
» déjà fourni au semestre 3, permettant de réinitialiser l'«EntityManager
» avant chaque test pour éviter les échecs en cascade. Notez que ces deux «Helper
» dépendent du module «Symfony
» deCodeception
.
Annonces d'un utilisateur ¶
Lors de la recherche dans les annonces, il peut être intéressant de consulter les annonces d'un utilisateur, un cas particulier étant les annonces de l'utilisateur connecté. Il faut donc ajouter cette fonctionnalité.
Travail à réaliser
- Créez une action permettant d'afficher les annonces d'un utilisateur dans un contrôleur dédié
- Affichez les annonces de l'utilisateur paginées et triées par date de création décroissante, en réutilisant le fragment de « template » réalisé plus tôt
- Lorsque la liste d'annonces est celle de l'utilisateur connecté, le titre de la page doit être «
Mes annonces
» - Une entrée de menu «
Mes annonces
» doit permettre d'accéder aux annonces de l'utilisateur connecté - Transformez le prénom de l'utilisateur dans les détails d'une annonce en un lien permettant de consulter ses annonces
- Complétez les tests de fumée
Création d'un « Voter
»
¶
Symfony
propose la notion de « Voter
» pour gérer les autorisations. Ce concept permet de centraliser la logique des autorisations plutôt que d'effectuer des tests à divers endroits du code. Vous allez créer un « Voter
» vérifiant les autorisations de modification et de suppression d'une annonce basées sur le contrôle du propriétaire de l'annonce.
Travail à réaliser
- Utilisez le «
MakerBundle
» pour générer un «Voter
» - Adaptez le «
Voter
» pour vérifier les autorisations de modification et de suppression d'une annonce par son propriétaire uniquement - Utilisez le «
Voter
» pour cloisonner les actions de modification et de suppression d'une annonce - Utilisez le «
Voter
» pour afficher les boutons de modification et de suppression d'une annonce uniquement au propriétaire de ladite annonce - Ajustez les tests de modification et de suppression d'une annonce
- Ajoutez des tests permettant de contrôler qu'un utilisateur ne peut ni modifier ni supprimer une annonce qui ne lui appartient pas
Inscription d'un nouvel utilisateur dans l'application ¶
Les visiteurs anonymes doivent pouvoir s'inscrire pour profiter des fonctionnalités de l'application. Il leur sera alors demandé de confirmer leur adresse mail avant de pouvoir utiliser les fonctionnalités nécessitant d'être connecté.
Formulaire d'inscription ¶
Le « MakerBundle
» permet de générer un formulaire d'inscription avec différentes options selon vos réponses aux questions posées. Vous allez donc générer un formulaire d'inscription et l'adapter à vos besoins.
Travail à réaliser
- Utilisez le «
MakerBundle
» pour générer un formulaire d'inscription avec les spécificités suivantes :- Envoyer un mail de confirmation d'inscription
- Inclure l'identifiant de l'utilisateur dans le lien de confirmation contenu dans le mail
- Authentifier l'utilisateur après son inscription
InformationLisez bien le compte-rendu de la commande pour savoir comment finaliser le formulaire d'inscription.
- Supprimez le champ généré « You should agree to our terms. » du formulaire d'inscription
- Traduisez en français le formulaire d'inscription, le modèle de mail et les messages des contraintes
- Ajoutez un lien vers l'inscription dans la page de connexion
- Ajoutez les nom et prénom de l'utilisateur au formulaire d'inscription Information
Respectez les bonnes pratiques en déléguant la responsabilité de présentation des «
labels
» à la vue. - Améliorez l'ergonomie de la saisie des champs du formulaire d'inscription en imposant en
HTML
les mêmes contraintes que celles définies dans l'entité «User
»InformationLe mot de passe reçoit l'attribut
HTML
«autocomplete="new-password"
» pour indiquer au navigateur qu'il s'agit d'un nouveau mot de passe et qu'il ne doit pas proposer la saisie semi-automatique. - Complétez les tests de fumée
Configuration des services d'envoi et de consultation de mails ¶
Le formulaire d'inscription que vous avez généré doit conduire à l'envoi d'un mail lorsque l'utilisateur valide sa saisie. Ceci va nécessiter la configuration de « Messenger
» et de « Mailer
» ainsi que la prise en main de Mailpit
pour consulter les mails envoyés durant les essais en phase de développement.
Travail à réaliser
- Exposez les ports de
Mailpit
dans le «compose.override.yaml
» de l'application - Déportez la définition du mail et du nom de l'émetteur dans un fichier «
.env
» et utilisez les variables d'environnement dans la configuration de «Mailer
»InformationPensez à supprimer le «
from()
» dans le contrôleur d'inscription. - Désactivez la livraison des mails dans le fichier «
.env
» - Modifiez le routage des mails pour qu'il soit synchrone, uniquement en environnement de développement
- Vérifiez que l'inscription d'un nouvel utilisateur conduit bien à la réception d'un mail dans
Mailpit
- Utilisez le lien contenu dans le mail de confirmation et vérifiez que l'utilisateur est bien validé (champ «
is_verified
» de la table)
Validation de l'adresse mail du nouvel l'utilisateur ¶
Le mail envoyé à l'utilisateur contient un lien permettant de valider son inscription. Il se peut que l'utilisateur ne reçoive pas le mail ou qu'il le supprime par mégarde. Il faut donc prévoir une fonctionnalité permettant de renvoyer le mail de confirmation.
Tant que l'utilisateur n'a pas validé son adresse mail, il ne doit pas pouvoir utiliser les fonctionnalités nécessitant d'être connecté. Juste après l'inscription, l'utilisateur est automatiquement connecté à l'application. Il est donc identifié et sera systématiquement redirigé vers la page de demande de confirmation de son adresse mail. Ceci permet de le forcer à valider son adresse mail. Dans un cas réel, il serait préférable de laisser l'utilisateur accéder aux parties publiques de l'application tant qu'il n'a pas validé son inscription.
Travail à réaliser
- Créez une action «
validateUserMail()
» dans le contrôleur «Registration
» permettant d'afficher à l'utilisateur connecté la demande de la confirmation de son adresse mail - Définissez le code de ce nouveau contrôleur afin qu'il :
- redirige l'utilisateur connecté vers la page d'accueil si son adresse mail est déjà validée
- construise un formulaire de demande de l'envoi du mail de confirmation de l'adresse mail de l'utilisateur
- redirige simplement l'utilisateur vers la page d'accueil si le formulaire est soumis (vous modifierez ce comportement plus tard)
- effectue le rendu de la vue dans les autres cas
- Utilisez le «
MakerBundle
» pour générer un «EventListener
» «ForceValidateMailListener
» qui intercepte toutes les requêtes faites sur votre application et qui redirige vers la page de demande de confirmation de l'adresse mail si l'utilisateur n'a pas validé son adresse mailInformationVérifiez que la requête interceptée est bien la requête principale de l'application et non une sous-requête.
Ne redirigez pas les routes utiles à l'utilisateur dont l'adresse mail n'est pas validée, comme la page de confirmation de l'adresse mail ou la page de déconnexion.
Vous aurez besoin de générer des URL à partir de noms de routes.
- Complétez les tests de fumée
Événement d'inscription d'un nouvel l'utilisateur ¶
La gestion de l'envoi d'un mail de confirmation d'inscription et de validation de l'adresse mail d'un utilisateur sont des fonctionnalités qui peuvent être réutilisées dans d'autres contextes de l'application. Il est donc intéressant de les découpler du contrôleur de gestion des inscriptions. En effet, l'envoi d'un mail de confirmation d'inscription n'est qu'une conséquence souhaitée de l'inscription d'un nouvel utilisateur. Un nouvel utilisateur pourrait être inscrit dans d'autres circonstances et d'autres actions pourraient être déclenchées à cette occasion. Il est donc intéressant de créer un événement « UserRegistered
» qui sera déclenché à l'inscription d'un nouvel utilisateur. Un « EventListener
» personnalisé pourra alors intercepter les événements « UserRegistered
» et envoyer un mail de demande de validation de l'adresse mail de l'utilisateur. Si l'utilisateur demande une nouvelle fois l'envoi du mail de confirmation, un événement « UserConfirmationEmailNotReceived
» sera déclenché. Il sera alors également intercepté par le « EventListener
» qui enverra un nouveau mail de confirmation d'inscription.
Travail à réaliser
- Consultez le support de cours de Floran Brutel et Nicolas Hart sur les événements et les écouteurs
- Créez la classe d'événement «
UserRegisteredEvent
» - Créez la classe d'événement «
UserConfirmationEmailNotReceivedEvent
» - Créez un «
EventListener
» «EmailVerifierListener
» qui intercepte les événements «UserRegisteredEvent
» et «UserConfirmationEmailNotReceivedEvent
» pour envoyer un mail de demande de validation de l'adresse mail à l'utilisateurInformationLe code d'envoi du mail sera recopié à l'identique depuis de contrôleur d'inscription.
- Déclenchez un événement «
UserConfirmationEmailNotReceived
» lors de l'appui sur le bouton de demande de renvoi du mail de confirmation - Déclenchez un événement «
UserRegistered
» à la création d'un nouvel utilisateur lors de l'inscription (en remplacement de l'envoi du mail)
Gestion des messages « flash » ¶
Les diverses actions de l'application doivent pouvoir informer l'utilisateur du résultat de l'opération. Il est donc nécessaire de mettre en place un système de messages « flash ». Certains messages sont déjà présents dans le code du contrôleur d'inscription, mais ils ne sont que partiellement fonctionnels. Vous allez donc les compléter et les afficher à l'utilisateur.
Travail à réaliser
- Modifiez les messages « flash » du contrôleur d'inscription pour qu'ils soient uniquement dans les catégories «
success
» et «error
» - Inspirez-vous du code de la vue «
register.html.twig
» pour afficher les messages « flash » «success
» et «error
» dans «base.html.twig
», avec des styles appropriés - Supprimez le code de gestion des messages flash de la vue «
register.html.twig
» - Ajoutez un message « flash » «
success
» lors de la demande d'inscription et de renvoi du mail de confirmation
Sécurisation du mot de passe de l'utilisateur ¶
Le mot de passe choisi par l'utilisateur lors de son inscription n'est pas confirmé par une double saisie. Ceci est une mauvaise pratique que vous devez corriger. De plus, la complexité du mot de passe choisi par l'utilisateur n'est pas évaluée. Vous allez donc ajouter une contrainte de complexité du mot de passe dans le formulaire d'inscription.
Travail à réaliser
- Modifiez le formulaire d'inscription pour qu'il demande la confirmation du mot de passe avec un champ «
RepeatedType
»InformationRespectez les bonnes pratiques en déléguant la responsabilité de présentation des «
labels
» à la vue. - Installez le bundle «
RollerworksPasswordStrength Validator
» - Ajoutez dans le formulaire d'inscription une contrainte de complexité du mot de passe afin qu'il contienne :
- au moins 10 caractères
- au moins une lettre minuscule et une lettre majuscule
- au moins un chiffre
- au moins un caractère spécial
Tests d'inscription ¶
La vérification de l'adresse mail de l'utilisateur et le choix de son mot de passe lors de l'inscription sont des étapes importantes de la sécurisation de votre application. Il est donc nécessaire de tester ces procédures.
Travail à réaliser
- Désactivez la livraison des mails durant les tests pour éviter de générer des envois («
MAILER_DSN=null://null
» dans «.env.test
») - Utilisez les ancres et références de
YAML
pour reproduire la configuration synchrone de «Mailer
» dans les environnements de développement et de testInformationVous pouvez vous inspirer du fichier «
config/packages/zenstruck_foundry.yaml
» pour la syntaxe. - Créez un «
Cest
» «Registration\RegisterCest
» - Testez les points clés de l'inscription :
- Saisie des données dans le formulaire d'inscription, réception du mail de confirmation et validation de l'adresse mail Information
Pour tester si un mail est envoyé, vous devez désactiver le suivi des redirections comme indiqué dans la documentation.
Vous devez utiliser le lien de vérification de l'adresse mail contenu dans le mail de confirmation. Pour cela, il faut extraire le contenu du mail. Vous pourrez ensuite extraire le lien de vérification de l'adresse mail à l'aide d'une expression régulière correspondant à la balise
HTML
du lien et capturant le contenu de l'attribut «href
». - Refus des données dans le formulaire d'inscription en cas confirmation de mot de passe erronée
- Refus des données dans le formulaire d'inscription en cas de mot de passe incompatible avec les contraintes de complexité
- Redirection systématique sur la page de confirmation de l'adresse mail si l'utilisateur n'a pas validé son adresse mail
- Saisie des données dans le formulaire d'inscription, réception du mail de confirmation et validation de l'adresse mail
Commandes de console ¶
Symfony
propose de nombreuses fonctionnalités à travers le script « bin/console
». Ces commandes sont créées à partir du composant « Console
» et peuvent être enrichies par des commandes personnalisées. Vous allez créer une commande personnalisée permettant de supprimer les utilisateurs dont l'adresse mail n'a pas été vérifiée depuis un nombre de jours donné.
Date d'inscription d'un utilisateur ¶
La date d'inscription des utilisateurs n'est pas mémorisée. Vous allez donc ajouter cette information dans l'entité « User
» en faisant une nouvelle fois usage de « Timestampable
».
Travail à réaliser
- Ajoutez une propriété obligatoire «
registeredAt
» à l'entité «User
» - Rendez la nouvelle propriété «
Timestampable
» à la création - Ajustez la «
Factory
» pour que tout utilisateur créé aléatoirement ait une date d'inscription comprise entre 1 jour et 2 mois avant la date du jour - Modifiez la «
Story
» pour ajouter un «Pool
» «unverified users
» de 4 utilisateurs non vérifiés
Ajout de fonctionnalités au « Repository
» des utilisateurs
¶
Maintenant que vous disposez de la date d'inscription des utilisateurs, vous devez pouvoir sélectionner et supprimer des utilisateurs non vérifiés en fonction du nombre de jours écoulés depuis leur inscription. Ces fonctionnalités sont à la charge du « Repository
» des utilisateurs et vous allez les écrire.
Travail à réaliser
- Ajoutez une méthode «
findUnverifiedUsersSince()
» au «Repository
» des utilisateurs permettant de sélectionner les utilisateurs non vérifiés depuis un nombre de jours donné facultatif - Ajoutez une méthode «
deleteUnverifiedUsersSince()
» au «Repository
» des utilisateurs permettant de supprimer les utilisateurs non vérifiés depuis un nombre de jours donné facultatifInformationLes méthodes étant très similaires, factorisez tout le code qui peut l'être.
Création de la commande de suppression des utilisateurs non vérifiés ¶
Vous allez créer une commande personnalisée permettant de supprimer les utilisateurs dont l'adresse mail n'a pas été vérifiée depuis un nombre de jours donné.
Par défaut, la commande affiche tous les utilisateurs non vérifiés. Vous pouvez filtrer les utilisateurs à considérer en précisant le nombre de jours depuis lequel l'utilisateur n'a pas vérifié son adresse mail avec l'option « --days=DAYS
». Une option « --delete
» permet de supprimer les utilisateurs listés, après confirmation. Une dernière option « --force
» permet de supprimer les utilisateurs listés sans confirmation.
Travail à réaliser
- Utilisez le «
MakerBundle
» pour générer une commande personnalisée «app:purge-registration
» - Ajoutez les options «
--days=DAYS
», «--delete
» et «--force
» à la commande - Vérifiez que l'option «
--days=DAYS
» est bien un nombre entier positif, si elle est présente - Réalisez l'affichage sous forme de tableau des utilisateurs non vérifiés (nom, prénom, adresse mail et nombre de jours écoulés) depuis le nombre de jours donné facultatif
- Supprimez, après confirmation, les utilisateurs non vérifiés depuis le nombre de jours donné facultatif si l'option «
--delete
» est présente - Outrepassez la confirmation de suppression si l'option «
--force
» est présente - Affichez le nombre d'utilisateurs supprimés en cas de suppression effective
Tests de la commande de suppression des utilisateurs non vérifiés ¶
Votre commande de suppression des utilisateurs non vérifiés effectue des opérations critiques puisqu'elle supprime des utilisateurs. Il est primordial de la tester.
Travail à réaliser
- Générez un « Cest » «
Command\PurgeRegistration
» - Lisez la documentation de «
runSymfonyConsoleCommand
» - Testez la commande de suppression des utilisateurs non vérifiés avec ou sans les options «
--days=DAYS
», «--delete
» et «--force
» :- Affichage des utilisateurs non vérifiés
- Suppression des utilisateurs non vérifiés (et non suppression des autres utilisateurs)
Réalisation d'un système de « likes » ¶
L'une des forces d'un développeur est d'être capable de transposer une fonctionnalité déjà implémentée dans un nouveau projet. La plus grande force d'un bon développeur est d'être capable d'acquérir de nouvelles compétences en s'appuyant sur celles qu'il possède. Vous allez donc réaliser une fonctionnalité de « likes » en vous appuyant sur les connaissances acquises lors des semestres précédents et dans ce projet. Vous serez peu guidé lorsque la tâche fait appel à des connaissances déjà acquises. Vous serez néanmoins guidé lorsque la tâche fait appel à des connaissances en cours d'acquisition.
Formulation de la demande client ¶
La demande du client est formulée comme une liste de règles de fonctionnement :
- les utilisateurs peuvent « liker » les annonces
- un utilisateur ne peut « liker » une même annonce qu'une seule fois et peut supprimer ce « like » s'il le désire
- les visiteurs voient le nombre de « likes » d'une annonce dans la liste des annonces et sur le détail de l'annonce
- les utilisateurs voient s'ils ont « liké » ou non une annonce particulière en plus du nombre de « likes » dans la liste des annonces et sur le détail de l'annonce
- un utilisateur ne peut pas « liker » ses propres annonces
Le client propose un visuel pour afficher les « likes » :
Adapter le modèle de données ¶
La demande du client doit être traduite en termes de modèle de données. Vous allez donc l'adapter pour prendre en compte les « likes ».
Travail à réaliser
- Réfléchissez à la manière dont vous allez modéliser les « likes »
- Transcrivez ces modifications dans le modèle de données
- Effectuez une migration de la base de données
- Créez de nouvelles « fixtures » pour les « likes » afin que l'utilisateur factice « user@example.com » ainsi que tous les utilisateurs aléatoires aient « liké » entre 50 et 100 annonces aléatoires Information
Les annonces « likées » par un utilisateur doivent être différentes des annonces qu'il a créées.
Construire un composant Twig
¶
Le nombre de « likes » d'une annonce doit être affiché dans la liste des annonces et sur le détail de l'annonce. Vous allez donc construire un composant Twig
permettant d'afficher ces informations en reprenant le visuel proposé par le client.
Travail à réaliser
- Lisez l'introduction des «
Twig Components
» - Installez les «
Twig Components
» avecComposer
- Utilisez la commande du «
MakerBundle
»Symfony
pour générer un composantTwig
«AdvertisementLikes
» - Ajoutez un «
Advertisement
» comme «Props
» du composantInformationDans l'exemple de la documentation, les «
Props
» sont de simples «string
». Vous pouvez naturellement utiliser un «Advertisement
» comme «Props
» de votre composant. - Ajoutez une méthode «
getLikesCount()
» au composant pour récupérer le nombre de « likes » de l'annonceInformationIl suffit de compter le nombre d'utilisateurs ayant « liké » l'annonce, les «
Collection
» de Doctrine implémentent l'interface «Countable
» - Structurez la vue du composant pour qu'elle affiche le nombre de « likes » de l'annonce précédé d'un cœur Information
Dans la vue, vous devez utiliser «
this
» pour accéder au composant et donc à ses propriétés et méthodes. - Ajoutez le composant dans la liste des annonces et sur le détail de l'annonce
- Ajoutez une méthode «
isLikedByUser()
» au composant pour savoir si l'utilisateur a « liké » l'annonce - Modifiez la vue du composant pour qu'elle affiche le nombre de « likes » de l'annonce précédé d'un cœur plein si l'utilisateur a « liké » l'annonce ou d'un cœur si l'utilisateur n'a pas « liké » l'annonce Information
Pensez à étudier le cas où aucun utilisateur n'est connecté.
Vue des annonces préférées de l'utilisateur ¶
L'objectif des « likes » est de mettre en avant une annonce en matérialisant l'intérêt qu'elle suscite, mais ils permettent aussi à un utilisateur de marquer les annonces qu'il préfère. Il est donc logique de lui proposer une vue de ses annonces préférées.
Travail à réaliser
- Créez une nouvelle action «
liked
» dans le contrôleur «AdvertisementController
» - Créez une méthode «
queryLikedByUser…()
» dans le «Repository
» des annonces permettant de récupérer les annonces « likées » par un utilisateur donnéInformationCette méthode doit rester compatible avec une utilisation optimisée de la pagination.
- Construisez la vue associée à l'action
- Ajoutez une entrée « Annonces préférées » dans le menu de l'application
Rendre le composant « Live
»
¶
Symfony UX
propose des « Live Components
» qui peuvent s'actualiser selon les interactions de l'utilisateur. Il est ainsi possible de bénéficier de la souplesse et la puissance d'AJAX
dans l'interface utilisateur sans écrire de JavaScript
.
L'intérêt pour l'application sera de transformer le composant « AdvertisementLikes
» en composant « Live
» afin de pouvoir « liker » une annonce et mettre à jour le nombre de « likes » sans recharger la page.
Travail à réaliser
- Installez les «
Live Components
» avecComposer
InformationComme indiqué dans la documentation, n'oubliez pas d'installer le «
StimulusBundle
» ni les paquetsnpm
nécessaires. - Lisez le chapitre « Making your Component "Live" » de la documentation
- Transformez le composant «
AdvertisementLikes
» en composant «Live
» - Ajoutez une méthode «
toggleLike()
» au composant pour ajouter ou supprimer le « like » de l'annonce pour l'utilisateur connectéInformationPensez à étudier le cas où aucun utilisateur n'est connecté.
- Ajoutez un attribut
PHP
à la méthode «toggleLike()
» pour en faire une «LiveAction
» - Modifiez la vue du composant pour qu'elle appelle la méthode «
toggleLike()
» lors d'un clic sur le cœur contenu dans le composantInformationLe cœur peut être transformé en bouton Bootstrap, uniquement si un utilisateur est connecté, pour faciliter la gestion du clic.
Gestion des droits d'accès ¶
L'un des principes de fonctionnement souhaités demande à ce que l'utilisateur ne puisse pas « liker » ses propres annonces. L'application doit impérativement veiller au respect de cette règle. Vous allez donc compléter le « Voter
» « AdvertisementVoter
» pour gérer ce cas.
Travail à réaliser
- Ajoutez un attribut «
LIKE
» au «AdvertisementVoter
» pour gérer le droit de «liker
» les annonces dont l'utilisateur n'est PAS l'auteur - Utiliser le «
Voter
» pour limiter l'utilisation de la méthode «toggleLike()
» du composant «AdvertisementLikes
» - Utilisez le «
Voter
» pour désactiver le clic sur le cœur dans le composant «AdvertisementLikes
»
Optimisations ¶
Comme précédemment, l'accès à de nouvelles ressources liées à une entité entraine de nouvelles requêtes qu'il convient d'étudier pour déterminer s'il est nécessaire de les optimiser.
Travail à réaliser
- Trouvez ce qui engendre de nouvelles requêtes
- Optimisez les requêtes Information
Lorsque vous faites évoluer le comportement de certaines méthodes, il convient souvent de les renommer pour refléter ces changements. Pensez à la refactorisation de code accessible par le raccourci «
SHIFT+F6
» dePHPStorm
pour gagner en efficacité et en fiabilité. - Déterminez s'il existe dans l'application des requêtes équivalentes qui peuvent être optimisées
- Optimisez les éventuelles requêtes le nécessitant
Introduction d'un flux de travail (« workflow ») pour la gestion des annonces ¶
Les annonces de l'application sont actuellement gérées de manière très classique. Dès qu'une annonce existe, elle est visible par tous les utilisateurs. Le propriétaire d'une annonce peut la modifier ou la supprimer. Vous allez introduire un flux de travail pour la gestion des annonces afin de permettre aux utilisateurs de créer des annonces sans les publier immédiatement, d'archiver des annonces et de les marquer comme terminées. La mise en place d'un état pour les annonces, ainsi que les transitions possibles entre ces états, est une démarche classique dans une application. Ainsi, Symfony
propose un composant « Workflow
» qui permet de gérer ces états et transitions.
Description du flux de travail ¶
La description complète de la mécanique du flux de travail est fournie par le client :
- une annonce nouvellement crée est dans un état « brouillon » : «
draft
» - le propriétaire de l'annonce « brouillon » peut la publier, elle passe alors dans l'état « publiée » : «
published
» - le propriétaire de l'annonce « publiée » peut la marquer comme terminée, elle passe alors définitivement dans l'état « terminée » : «
closed
» - le propriétaire de l'annonce « publiée » peut l'archiver, elle passe alors dans l'état « archivée » : «
archived
» - le propriétaire de l'annonce « brouillon » peut la supprimer, elle est alors supprimée de la base de données
- le propriétaire de l'annonce « archivée » peut la republier, elle passe alors dans l'état « publiée » : «
published
»
L'ensemble des transitions possibles peut être représenté par le diagramme suivant :
Notez que le propriétaire d'une annonce ne peut pas la modifier si elle est dans l'état « terminée ».
Travail à réaliser
- Parcourez la documentation de «
Workflow
» - Installez le composant «
Workflow
» avecComposer
- Configurez le composant «
Workflow
» pour gérer les états et transitions décrits ci-dessus - Modifiez l'entité «
Advertisement
» pour qu'elle mémorise un état - Définissez des constantes pour les états («
STATE_*
») et transitions («TRANSITION_*
») dans l'entité «Advertisement
» - Utilisez les nouvelles constantes dans la configuration
YAML
du composant «Workflow
»
Utilisation du flux de travail ¶
Le flux de travail défini va engendrer des modifications dans l'application. Vous allez donc les réaliser.
Travail à réaliser
- Adaptez les « fixtures » pour que les annonces créées aléatoirement soient dans l'état « publiée »
- Modifiez les annonces associées à l'utilisateur factice « user@example.com » pour qu'elles soient aléatoirement dans tous les états possibles
- Modifiez les requêtes du «
Repository
» des annonces pour qu'elles ne retournent que les annonces dans l'état « publiée » - Modifiez le «
Voter
» «AdvertisementVoter
» pour qu'il ne permette pas de consulter des annonces non publiées, sauf leur auteur et à condition qu'elles ne soient pas terminées - Ajoutez la consultation des annonces dans l'état « brouillon » pour l'utilisateur connecté
- Ajoutez au «
Voter
» «AdvertisementVoter
» la capacité de contrôler qu'une annonce peut être « publiée », « terminée » ou « archivée », uniquement par son auteurInformationLe «
Workflow
» permet de contrôler les transitions possibles. Utilisez cette capacité plutôt que de contrôler vous-même l'état nécessaire à la transition.Pour accéder au service «
Workflow
», vous devez l'injecter dans le «Voter
» en utilisant l'attribut «Target
». - Modifiez le processus de suppression d'une annonce pour contrôler que l'annonce peut bien être supprimée (elle doit être dans l'état « brouillon »)
- Sur le modèle de la suppression, ajoutez la possibilité de :
- publier (ou republier) une annonce
- archiver une annonce
- terminer une annonce
- Ajoutez des tests