- Accueil
- Programmation Web S3
- Objectifs de la séance
- Préambule
- Utilisation du moteur PHP local
- Documentation
- Mise en place d'une application
Symfony
- Gestion des versions avec Git
- Création du projet dans
PhpStorm
- Outils de qualité de code (analyse statique de code)
- Documentation du projet
- Mise en place de scripts
Composer
- Désactivation de
Symfony UX Turbo
- Le routeur
- Les vues dans
Symfony
:Twig
- De la route à la vue
- De la vue à la route
- Introduction aux tests fonctionnels
- Configuration de l'accès base de données de l'application
- Consultation des contacts
- Mise en place de votre base de données, retour aux bonnes pratiques
- Structuration de l'application
- Recherche des contacts
- Catégories de contacts
- Requêtes personnalisées avec le «
QueryBuilder
» - Utilisation de formulaires
- Préparation du contrôleur
- Interface de modification d'un «
Contact
» existant - Présentation des formulaires
- Validation des données
- Persistance des modifications d'un «
Contact
» existant - Interface de création d'un «
Contact
» - Personnalisation des formulaires en
Twig
- Inclusion de template
Twig
- Interface de suppression d'un «
Contact
» - Liens depuis la liste des contacts et derniers réglages
- Utilisateurs, authentification et autorisations
- Gestion de l'application : « back-office » avec
EasyAdmin
- Et après ?
Objectifs de la séance ¶
- Créer un projet
Symfony
avec l'outil «symfony
» en ligne de commande (« Symfony CLI ») - Prendre en main
Symfony
dans sa version 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
- Mettre en place un « back-office » avec
EasyAdmin
Préambule ¶
L'objectif de ce TP est de vous familiariser avec le développement d'applications Symfony
. Le sujet vous accompagne dans la découverte du framework tout en essayant de se conformer aux bonnes pratiques de Symfony
.
Remarque importante
De nombreux points du sujet consistent en l'observation de fichiers ou de fonctionnements qui s'appuient sur des conventions liées au framework. Ne les négligez pas ! Le but n'est pas de vous faire suivre bêtement un tutoriel mais de vous amener à comprendre ce que vous faites.
Utilisation du moteur PHP local ¶
Vous allez utiliser le moteur PHP installé localement sur votre poste de travail. Vous déclencherez le moteur PHP local, directement ou indirectement de diverses manières :
- à l'aide de la commande
php
pour exécuter du codephp -r "code PHP"
- à l'aide de la commande
php
pour exécuter un programmephp -f un_script_PHP
ouphp un_script_PHP
- comme une commande grâce à l'utilisation du shebang en tête d'un programme PHP
bin/console cache:clear
Le programme «
console
» est fourni dansSymfony
, s'utilise comme un script shell, et est écrit en PHP. Sa première ligne est#!/usr/bin/env php
- comme une commande grâce à l'utilisation du shebang en tête d'un paquet PHP (fichier
.phar
)composer.phar update
- en mode server local à travers l'utilisation du serveur Web intégré à PHP
php -S localhost:8000 -t public/
PHP
lance ici un serveur Web local que vous allez interroger avec votre navigateur Web.
Documentation ¶
Vous aurez besoin de la documentation de Symfony
ainsi que de celle des diverses API :
Mise en place d'une application Symfony
¶
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-contacts
» 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-contacts
pour éviter la saturation du disque local :
rm -Rf /working/votre_login/symfony-contacts
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-contacts
git pull
- Effacer votre dépôt local et repartir de zéro
cd /working/votre_login
rm -Rf /working/votre_login/symfony-contacts
git clone https://iut-info.univ-reims.fr/gitlab/votre_login/symfony-contacts.git
cd /working/votre_login/symfony-contacts
- Réinitialiser votre dépôt local selon le dépôt distant
cd /working/votre_login/symfony-contacts
git fetch
git reset --hard origin/main
Ensuite, dans le répertoire de votre projet, vous devez et (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 de l'exécutable « symfony
»
¶
Le développement d'applications Symfony
peut être facilité par l'utilisation de l'outil « symfony
» en ligne de commande (« Symfony CLI »). Ce dernier peut contrôler que le système comporte tous les prérequis pour le développement d'applications Symfony
ou tester la présence de failles de sécurité connues.
Travail à réaliser
- Installez l'exécutable «
symfony
» qui contient le serveur Web local en lançant la commande suivante :wget https://get.symfony.com/cli/installer -O - | bash
--2022-09-16 15:27:24-- https://get.symfony.com/cli/installer Resolving get.symfony.com (get.symfony.com)... 13.32.145.22, 13.32.145.97, 13.32.145.64, ... Connecting to get.symfony.com (get.symfony.com)|13.32.145.22|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 6100 (6,0K) [binary/octet-stream] Saving to: ‘STDOUT’ - 100%[==================================================================>] 5,96K --.-KB/s in 0s 2022-09-16 15:27:24 (378 MB/s) - written to stdout [6100/6100] Symfony CLI installer Environment check [*] cURL is installed [*] Tar is installed [*] Git is installed [*] Your architecture (amd64) is supported Download Downloading https://github.com/symfony-cli/symfony-cli/releases/latest/download/symfony-cli_linux_amd64.tar.gz... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 5105k 100 5105k 0 0 15.4M 0 --:--:-- --:--:-- --:--:-- 81.7M Uncompress binary... Installing the binary into your home directory... The binary was saved to: /home/Users/cutron01/.symfony5/bin/symfony The Symfony CLI was installed successfully! Use it as a local file: /home/Users/cutron01/.symfony5/bin/symfony Or add the following line to your shell configuration file: export PATH="$HOME/.symfony5/bin:$PATH" Or install it globally on your system: mv /home/Users/cutron01/.symfony5/bin/symfony /usr/local/bin/symfony Then start a new shell and run 'symfony'
- Modifiez ou créez le fichier «
.profile
» (à la racine de votre compte) afin qu'il contienneexport PATH="$HOME/.symfony5/bin:$PATH"
- Chargez les modifications de votre «
.profile
»source ~/.profile
InformationLa configuration du shell Bash se fait à l'aide de fichiers de démarrage. Le fichier «
~/.profile
» est exécuté à chaque ouverture de session. Il est donc nécessaire de se (re)connecter pour que les modifications soient prises en compte dans chaque terminal lancé. Pour cette séance, vous pouvez vous contenter de recharger le fichier de configuration avec la commande «source ~/.profile
».Si vous souhaitez profiter immédiatement des bénéfices de la modifications du fichier «
.profile
», vous devez vous déconnecter de la machine et vous reconnecter. - Vérifiez le bon fonctionnement de l'exécutable «
symfony
»symfony self:version
- Contrôlez la compatibilité du système avec la commande :
symfony check:requirements --verbose
Symfony Requirements Checker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > PHP is using the following php.ini file: /etc/php/8.0/cli/php.ini > Checking Symfony requirements: [OK] iconv() must be available [OK] json_encode() must be available [OK] session_start() must be available [OK] ctype_alpha() must be available [OK] token_get_all() must be available [OK] simplexml_import_dom() must be available [OK] detect_unicode must be disabled in php.ini [OK] xdebug.show_exception_trace must be disabled in php.ini [OK] xdebug.scream must be disabled in php.ini [OK] PCRE extension must be available [OK] string functions should not be overloaded [OK] xdebug.max_nesting_level should be above 100 in php.ini [OK] PCRE extension should be at least version 8.0 (10.4 installed) [OK] PHP-DOM and PHP-XML modules should be installed [OK] mb_strlen() should be available [OK] utf8_decode() should be available [OK] filter_var() should be available [OK] posix_isatty() should be available [OK] intl extension should be available [OK] intl extension should be correctly configured [OK] intl ICU version should be at least 4+ [OK] intl.error_level should be 0 in php.ini [OK] a PHP accelerator should be installed [OK] short_open_tag should be disabled in php.ini [OK] magic_quotes_gpc should be disabled in php.ini [OK] register_globals should be disabled in php.ini [OK] session.auto_start should be disabled in php.ini [OK] xdebug.max_nesting_level should be above 100 in php.ini [OK] "memory_limit" should be greater than "post_max_size". [OK] "post_max_size" should be greater than "upload_max_filesize". [OK] PDO should be installed [OK] PDO should have some drivers installed (currently available: mysql, sqlite) [OK] Your system is ready to run Symfony projects Note The command console can use a different php.ini file ~~~~ than the one used by your web server. Please check that both the console and the web server are using the same PHP version and configuration.
InformationDans un environnement Windows, vous pouvez télécharger une archive
zip
qui contient «symfony.exe
». Ce programme est l'exécutable de l'outil que vous devez donc placer dans un répertoire de votre système et le rendre accessible en ligne de commande (le répertoire doit figurer dans la variable d'environnement «Path
»).
Création d'un 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
- Si ce n'est pas déjà fait, installez
Composer
InformationL'outil «
symfony
» en ligne de commande (« Symfony CLI ») utiliseComposer
pour créer le nouveau projet. S'il n'est pas disponible sur le système, une version temporaire sera téléchargée. Il est plus rapide et logique d'avoir votre propre version fonctionnelle qui sera par ailleurs utile par la suite. - Vérifiez que
Composer
fonctionne correctement :composer about
- Mettez à jour
Composer
:composer self-update
- 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 «6.3.*
» :symfony --version 6.3 --webapp new symfony-contacts
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 le répertoire de votre application
- Dans le terminal, lancez le serveur Web local avec la commande suivante :
symfony serve
InformationLe serveur Web fonctionnera tant que vous n'aurez pas terminé l'exécution l'outil ligne de commande «
symfony
» avec «CTRL+C
». - Suivez les instructions données dans la partie « Gestion du certificat du serveur Web de l'API dans le navigateur » du tutoriel « Configuration CORS pour utiliser l'authentification de l'API avec React »
- Accédez à l'URL «
https://127.0.0.1:8000
» pour obtenir : - Constatez l'apparition de la Web Debug Toolbar
- Observez les sorties texte du serveur Web local
- Naviguez rapidement à travers les outils de la Web Debug Toolbar
- Observez les sorties texte du serveur Web local
- 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-contacts
- Constatez que le dépôt
Git
local a été initialisé par l'outil «symfony
» en ligne de commande (« Symfony CLI »)git log
- Excluez du suivi de
Git
le répertoire «.idea
» dePhpStorm
echo -e "\n/.idea/" >> .gitignore
git add .gitignore
git commit -m "Exclusion du répertoire .idea"
Information
Si vous créez le projet avec Composer
, vous devez effectuer les actions suivantes :
- Initialisez votre dépôt
Git
git init
- Ajoutez l'ensemble des fichiers à l'index
git add .
- Effectuez la première validation
git commit -m "Initial commit"
- Renommez la branche principale en «
main
» (une branche vide ne peut pas être renommée, c'est pourquoi ceci est fait après le premier «commit
»)git branch -m main
Dépôt distant ¶
Passons maintenant à la configuration du dépôt distant.
Travail à réaliser
- Créez un nouveau projet «
symfony-contacts
» surGitLab
(pensez à décocher la case « Initialize repository with a README ») - Associez le dépôt local et le dépôt distant
- Poussez votre branche locale
Remarque importante
Dans toute la suite du TP, pensez à effectuer des « commit
» réguliers (un par question au minimum).
Vous pouvez utiliser PhpStorm
pour les commandes Git
« commit
» et « push
». PhpStorm
propose une option « Commit and Push… » particulièrement adaptée à votre situation :
N'oubliez pas d'ajouter les nouveaux fichiers au gestionnaire de versions (Ctrl+Alt+A
) avant vos « commit
». Vous pouvez constater que le nom des fichiers s'affiche en vert pour ceux qui ont été ajoutés au suivi par opposition au rouge pour ce qui n'ont pas été ajoutés et gris clair pour ceux qui sont ignorés.
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 . &
InformationCette commande est la façon la plus simple et la plus rapide de créer un nouveau projet
PhpStorm
dans un répertoire. Il n'est pas utile de la lancer une fois le projet créé puisque vos anciens projets sont proposés à l'ouverture dePhpStorm
ou dans son menu « File » puis « Recent Projects ». - Patientez pendant l'indexation des fichiers du projet visible dans la partie droite de la barre d'état
- Activez le greffon «
Symfony Support
» si ce n'était pas déjà fait et que cela vous est proposé
Outils de qualité de code (analyse statique de code) ¶
Vous allez mettre en place des outils d'analyse statique de code pour garantir la qualité de votre application. Comme l'année dernière, vous utiliserez PHP CS Fixer
pour vérifier et corriger le style de votre code PHP
. Vous utiliserez un outil équivalent pour le code Twig
.
Travail à réaliser
- Installez
PHP CS Fixer
en vous référant au tutoriel Installation et configuration dePhpStorm
pour configurerPHP CS Fixer
- Rendez-vous dans les préférences de
PhpStorm
dans « PHP → Quality Tools » et vérifiez quePHP CS Fixer
est bien configuré - Installez
Twig CS Fixer
, en pensant à exécuter la recette (recipe) :composer require --dev vincentlanglet/twig-cs-fixer
./composer.json has been updated Running composer update vincentlanglet/twig-cs-fixer Loading composer repositories with package information Updating dependencies Lock file operations: 1 install, 0 updates, 0 removals - Locking vincentlanglet/twig-cs-fixer (2.12.1) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 1 install, 0 updates, 0 removals - Installing vincentlanglet/twig-cs-fixer (2.12.1): Extracting archive Generating autoload files 125 packages you are using are looking for funding. Use the `composer fund` command to find out more! Symfony operations: 1 recipe (c941cb0116eec3d0ad57af4541444f07) - WARNING vincentlanglet/twig-cs-fixer (>=0.6): 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/vincentlanglet/twig-cs-fixer/0.6 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 <-- répondre y ici - Configuring vincentlanglet/twig-cs-fixer (>=0.6): From github.com/symfony/recipes-contrib:main Executing script cache:clear [OK] Executing script assets:install public [OK] What's next? Some files have been created and/or updated to configure your new packages. Please review, edit and commit them: these files are yours. No security vulnerability advisories found. Using version ^2.12 for vincentlanglet/twig-cs-fixer
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
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:phpcs
» qui lance la commande de vérification du code parPHP CS Fixer
- Ajoutez un script «
fix:phpcs
» qui lance la commande de correction du code parPHP CS Fixer
- Ajoutez un script «
test:twigcs
» qui lance la commande de vérification du code parTwig CS Fixer
- Ajoutez un script «
fix:twigcs
» qui lance la commande de correction du code parTwig CS Fixer
- Documentez vos scripts dans le fichier «
composer.json
» - Documentez vos scripts dans le fichier «
README.md
» - Validez votre travail dans
Git
- Relancez votre serveur Web local à l'aide du script
Composer
que vous venez d'écrire
Désactivation de Symfony UX Turbo
¶
Depuis plusieurs versions 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
. Le principal avantage est que les ressources liées (images, fichiers JavaScript
, fichiers CSS
, …) à la ressource principale (celle affichée dans la barre d'adresse du navigateur) ne sont pas rechargées à chaque changement d'URL. Ceci accélère significativement les temps de chargement et diminue la bande passante nécessaire. Cependant, dans le cadre de l'apprentissage des mécaniques Web, le fonctionnement de Hotwire Turbo
transforme les intéractions client/serveur et perturbe votre compréhension du fonctionnement de Symfony
. Vous allez donc le désactiver..
Travail à réaliser
- Ouvrez le fichier «
assets/controllers.json
» - Désactivez le contrôleur
Stimulus
«@symfony/ux-turbo
» en passant la valeur de l'attribut «enabled
» à «false
»:{ "controllers": { "@symfony/ux-turbo": { "turbo-core": { "enabled": false, "fetch": "eager" …
Le routeur ¶
Dans le framework Symfony
, un unique programme « public/index.php
» est lancé pour chaque requête HTTP
vers votre application et il est dépendant de l'environnement, par défaut « dev
» pour développement et « prod
» pour production. Le système de routage entre alors en jeu pour permettre de réaliser des tâches différentes en fonction de la requête HTTP
soumise à l'application.
Travail à réaliser
Lisez les premiers paragraphes de la documentation sur le routage jusqu'à la fin de « Creating Routes as Attributes » et faites le lien avec la représentation graphique du flux applicatif :
Les routes actuelles ¶
Les routes disponibles sont potentiellement différentes en fonction de l'environnement de l'application. Il existe, par défaut, des routes accessibles dans les environnements « prod
» et « dev
» et des routes spécifiques à l'environnement de développement.
Travail à réaliser
- Observez les routes disponibles dans l'environnement de production :
bin/console debug:router --env=prod
- Observez la configuration générale du routage en ouvrant le fichier «
config/routes.yaml
» qui décrit que le routage est défini dans les attributs de vos contrôleurs - Observez les routes disponibles dans l'environnement de développement, qui est celui par défaut dans votre configuration :
bin/console debug:router
Les routes disponibles dans l'environnement de développement sont actuellement, dans cet ordre :
- Une route pour les erreurs
Twig
- une route «
_wdt
» qui correspondent à la « Web Debug Toolbar » - celles commençant par «
_profiler
» qui sont destinée au « Profiler » (profilage de l'application lors de son exécution)
- Une route pour les erreurs
- Observez la configuration du routage « Profiler » en ouvrant le fichier «
config/routes/web_profiler.yaml
» - Vous constatez que les routes sont définies dans la configuration du «
WebProfilerBundle
» et uniquement dans l'environnement de développement avec «when@dev
» en début de configuration
Liens entre route et contrôleur ¶
Chaque route définie permet de déclencher une action d'un contrôleur (« Controller
»). Une action est une méthode publique d'un contrôleur qui est associée à une URI
, généralement à l'aide d'un attribut « Route
» qui précède la méthode.
Les fichiers des contrôleurs sont localisés dans le répertoire « Controller
» et sont nommés « xxxController.php
», « xxx
» étant le nom du contrôleur. Les contrôleurs font partie de l'espace de nom « App\Controller
» ou d'un sous espace de nom de ce dernier. Ils héritent possiblement de « Symfony\Bundle\FrameworkBundle\Controller\AbstractController
».
Travail à réaliser
- Complétez vos connaissances en lisant au moins la partie « Creating Routes as Attributes » de la documentation.
- Créez un nouveau contrôleur «
HelloController
» à l'aide du « MakerBundle » :bin/console make:controller Hello
InformationLe «
MakerBundle
» peut fonctionner en mode interactif ou à l'aide de paramètres de la ligne de commande. Ici, vous avez fourni le paramètre «Hello
» qui correspond au nom du contrôleur que vous souhaitez. Ce dernier sera donc nommé «HelloController
» car le «MakerBundle
» ajoute automatiquement le suffixe «Controller
» que vous n'aviez pas précisé. - Ouvrez le fichier du contrôleur dans
PhpStorm
- Observez l'ensemble des routes disponibles pour votre application avec la commande
bin/console debug:router
- Observez l'ensemble des routes disponibles pour votre application ainsi que les contrôleurs associés avec la commande
bin/console debug:router --show-controllers
- Faites le lien entre la première route disponible et le code généré par le «
MakerBundle
» - Observez les détails de la route nommée «
app_hello
» grâce à la commandebin/console debug:router app_hello
- Ajoutez l'action et la route associée qui suivent dans votre contrôleur :
#[Route('/hello/world')] public function world(): Response { return new Response('Hello world!'); }
- Notez que cette route ne comporte pas de nom dans l'attribut «
#[Route(
» et faites le lien avec le nom généré automatiquement que vous observez à l'aide de la commandebin/console debug:router
- Essayez l'URL «
http://localhost:8000/hello/world
» - Regardez le code source
HTML
du contenu reçu par votre navigateur - Essayez l'URL «
http://localhost:8000/hello/guys
» - Constatez la réponse reçue par votre navigateur et son code
HTTP
Route contenant un paramètre ¶
Les routes ne sont pas nécessairement définies à partir d'un URI
fixe qui permet de déclencher une action d'un contrôleur. Il est en effet possible de rendre paramétriques des parties de l'URI
.
Travail à réaliser
- Complétez vos connaissances en lisant au moins la partie « Route Parameters » de la documentation.
- Remplacez le texte «
world
» du chemin «/hello/world
» de la route par un paramètre «name
» - Adaptez le prototype de l'action «
world()
» afin qu'elle puisse recevoir la valeur du paramètre «name
» de la route - Observez la nouvelle formulation de la route de votre application avec la commande
bin/console debug:router
- Remplacez le mot «
world
» de la chaîne «Hello world!
» par la valeur du paramètre de l'actionInformationCe que vous venez de faire est extrêmement dangereux puisque toute chaîne fournie dans l'
URL
va se retrouver directement dans la réponse envoyée au navigateur, constituant ainsi une possibilité d'attaque XSS reflétée. Il faudrait échapper la valeur de la variable «$name
», comme cela vous a déjà été démontré à plusieurs reprises dans les précédents sujets de TP. - Essayez l'
URL
«http://localhost:8000/hello/world
» - Essayez l'
URL
«http://localhost:8000/hello/bob
» - Constatez la réponse reçue par votre navigateur et son code
HTTP
- Remarquez le lien entre le paramètre «
name
» du chemin de la route et le paramètre «$name
» de l'action
Les vues dans Symfony
: Twig
¶
Maintenant que vous avez rendu paramétrique l'action world()
du contrôleur HelloController
, vous allez lui associer le rendu d'une vue Twig
.
Web Debug Toolbar ¶
La « Web Debug Toolbar » n'est activée que si la vue contient la balise de fin de corps d'une page Web </body>
.
Travail à réaliser
- Saisissez l'
URL
«http://localhost:8000/hello/world
» dans votre navigateur - Dans l'action «
world()
» du «HelloController
», demandez le rendu d'un modèleTwig
en remplaçant l'instructionreturn new Response('…
parreturn $this->render('hello/world.html.twig');
- Essayez l'
URL
«http://localhost:8000/hello/world
» - Constatez l'erreur signalée par
Symfony
ainsi que le code réponseHTTP
reçu par votre navigateur - Créez le fichier «
world.html.twig
» manuellement en effectuant un clic droit sur le répertoire «templates/hello
» dansPhpStorm
et en utilisant le menu « New » puis « File » - Essayez l'
URL
«http://localhost:8000/hello/world
» - Constatez la réponse vide reçue par votre navigateur
- Ajoutez le texte «
Hello world!
» dans le fichier «templates/hello/world.html.twig
» - Essayez l'
URL
http://localhost:8000/hello/world
- Constatez que la « Web Debug Toolbar » n'est pas présente
- Regardez le code source
HTML
du contenu reçu par votre navigateur - Faites en sorte que le fichier «
templates/hello/world.html.twig
» propose une structureHTML
correcte dont le titre sera «Hello World!
» et qui contiendra le texte «Hello World!
» - Essayez l'
URL
«http://localhost:8000/hello/world
» - Notez l'apparition de la « Web Debug Toolbar ».
- Cliquez sur un des éléments de la « Web Debug Toolbar » pour accéder au profileur
- Naviguez dans les pages du profileur afin d'en découvrir les possibilités
Transmettre des données à la vue ¶
La vue a pour objectif de présenter les données à l'utilisateur. Vous allez donc découvrir comment transmettre des données au modèle Twig
et comment les exploiter.
Travail à réaliser
- Saisissez l'
URL
«http://localhost:8000/hello/bob
» dans votre navigateur - Lisez et observez les exemples de « Rendering Templates » et « Creating Templates »
- Passez la valeur du paramètre de l'action à la vue Information
Vous avez remarqué que le paramètre du chemin de la route «
{name}
» se retrouve sous le même nom «$name
» comme paramètre de l'action (et c'est d'ailleurs par rapport au nom queSymfony
fait « automagiquement » la correspondance entre les deux). Lorsque vous passez la même valeur à la vue, vous devez choisir le nom sous lequel la donnée sera manipulée dans le modèleTwig
. Afin de maintenir une logique globale et faciliter la compréhension de votre code, il est vivement conseillé de préserver le même nom pour l'utilisation dans le modèleTwig
. - Utilisez la valeur dans la vue pour produire «
Hello la_valeur_du_paramètre!
» dans un titre de niveau 1 et dans un paragraphe - Vérifiez le bon fonctionnement de cette nouvelle version de la vue dans votre navigateur Web
- Saisissez l'
URL
«http://localhost:8000/hello/bob<script>
» dans votre navigateur (oui, il y a bien «<script>
» à la fin de l'URL
!) - Observez le résultat dans le navigateur
- Observez le code source
HTML
du résultat dans le navigateurInformationTwig
échappe automatiquement les valeurs générées avec «{{ }}
» dans les « templates ».
Héritage et inclusion de « templates » Twig
¶
Vous venez de réaliser une vue à l'aide de « templates » Twig
. Cependant, la constitution de plusieurs vues ne devrait pas demander d'écrire une page Web complète pour chacune d'entre elles. Il est possible d'éviter ce genre de répétition en utilisant l'héritage ou l'inclusion de « templates ». Pour y parvenir, il convient de respecter les conventions de nommage et d'emplacement des modèle Twig
.
Travail à réaliser
- Modifiez votre vue «
world.html.twig
» afin qu'elle hérite de «base.html.twig
» - Remplacez la structure
HTML
de votre modèle par l'utilisation des blocs définis dans «base.html.twig
»InformationProfitez des suggestions de
PhpStorm
pour retrouver simplement les noms des blocs disponibles hérités du modèle «base.html.twig
». - Accédez à «
http://localhost:8000/hello/bob
» - Vérifiez le code source du résultat dans votre navigateur
Prise en main rapide de Twig
¶
Vous allez explorer quelques-unes des possibilités offertes par Twig
.
Travail à réaliser
- Utilisez une structure algorithmique
Twig
«for
» pour afficher 10 fois le paragraphe de salutation dans le nouveau « template » - Modifiez le texte produit pour y inclure le numéro du tour de boucle de
1
à10
en fin de ligne (le résultat est donc de la forme «Hello bob! (6)
») - Utilisez la fonction «
cycle
» deTwig
pour afficher alternativement en rouge puis vert (un attributHTML
«style=""
» suffira pour cet essai. - Ajoutez un filtre sur le nom afin qu'il apparaisse avec la première lettre en majuscule
- Installez le paquet
Composer
«twig/intl-extra
» pour bénéficier des fonctions d'internationalisation deTwig
- Formatez le nombre de
1
à10
pour qu'il soit systématiquement affiché sur deux caractères
De la route à la vue ¶
Vous allez créer une nouvelle route, une nouvelle action dans votre contrôleur ainsi qu'un nouveau « template » Twig
associé. L'action permettra d'afficher le message de salutation un nombre de fois donné.
Création d'une nouvelle route ¶
Vous allez ajouter une nouvelle route pour découvrir de nouvelles possibilités du routeur de Symfony
.
Travail à réaliser
- Consultez les règles de bonnes pratiques concernant les contrôleurs
- Créer une action vide «
manyTimes()
» dans le contrôleur «HelloController
»InformationEn saisissant «
pubf
» suivi de «TAB
» dans une classe,PhpStorm
génère automatiquementpublic function () { }
Vous êtes alors prêt à saisir le nom de la méthode et un nouvel appui sur
TAB
fait passer le focus sur la saisie des paramètres de la méthode. - Associez une route de chemin «
/hello/name/times
» paramétré par «name
» et «times
» à cette action en ajoutant l'attribut «#[Route(…
» - Si vous décidez de nommer votre route, utilisez le nommage standard «
app_nomducontroleur_nomdelaction
» (c'est un exemple !) - Modifiez votre action pour que ses paramètres soient en accord avec ceux du chemin de la route
- Consultez les règles de bonnes pratiques concernant le nommage des modèles
Twig
- Retournez le résultat la méthode «
render()
» du modèle «hello/many_times.html.twig
» - Utilisez les suggestions de
PhpStorm
(«Alt+Entrée
») pour générer automatiquement ce nouveau modèle :Remarque importanteSi la génération automatique du « template » n'est pas proposée par
PhpStorm
, c'est que vous n'avez pas installé le pluginSymfony
:que vous ne l'avez pas activé lorsque cela vous a été proposé :
ou qu'il n'est pas actif pour ce projet :
- Modifiez le code
Twig
en prenant «world.html.twig
» comme base :- Remplacez le titre de la page Web par «
Hello many times!
» - Remplacez le titre de niveau de la page Web par «
Hello many times valeur_du_paramètre_name!
» - Affichez «
times
» fois au lieu de «10
» fois le même paragraphe de salutation que dans «world.html.twig
»
- Remplacez le titre de la page Web par «
- Essayez l'
URL
«http://localhost:8000/hello/bob/5
» et vérifiez le bon fonctionnement
Ajout de conditions sur les paramètres ¶
Le routage de Symfony
permet de vérifier que les paramètres répondent à certaines conditions pour pouvoir déclencher une route.
Travail à réaliser
- Complétez la route pour que son second paramètre
times
soit un chiffre en utilisant la validation de paramètres - Essayez l'
URL
«http://localhost:8000/hello/bob/five
» et vérifiez que la validation du paramètre «times
» fonctionne (et donc que la route n'est pas acceptée !) - Vérifiez dans l'action du contrôleur que «
times
» est bien différent de zéro et inférieur ou égal à 10. Fixez sa valeur à 3 dans le cas contraire - Essayez les URL «
http://localhost:8000/hello/bob/5
», «http://localhost:8000/hello/bob/0
» ainsi que «http://localhost:8000/hello/bob/42
» et vérifiez la cohérence du résultat produit - Rendez le paramètre «
times
» optionnel qui vaudra par défaut «3
». - Utilisez la « Web Debug Toolbar » pour visualiser quelle est la route déclenchée par l'
URL
«http://localhost:8000/hello/bob
» - Faîtes en sorte que ce soit la route «
app_hello_manytimes
» qui soit déclenchée par l'URL
«http://localhost:8000/hello/bob
» (ceci doit mettre en évidence la façon dont les routes sont étudiées parSymfony
)
Redirection dans le contrôleur ¶
Les contrôles que vous venez d'effectuer conduisent à une forme d'incohérence de l'application. En effet, si vous demandez l'URL
« /hello/bob/42
», les conditions ajoutées dans le contrôleur vont limiter l'affichage à 3 messages alors que l'URL
contient toujours « 42
». Il serait souhaitable dans ce cas que le « 42
» de l'URL soit remplacé par « 3
». En raisonnant du côté serveur, il faut donc effectuer une première réponse à la requête de « /hello/bob/42
» qui demande une redirection vers « /hello/bob/3
» afin que le navigateur effectue une nouvelle demande de ressource, correcte cette fois.
Travail à réaliser
- Observez l'incohérence entre l'«
URL
» «/hello/bob/42
» demandée et les 3 messages affichés dans le navigateur - Lisez le chapitre concernant les redirections
- Effectuez une redirection dans l'action «
manyTimes()
» en demandant seulement «3
» affichages lorsque le paramètre «times
» est hors des bornes fixées précédemmentInformationSi vous tapez les guillemets du premier paramètre de la méthode «
redirectToRoute()
»,PhpStorm
doit vous proposer la liste de routes disponibles. - Vérifiez le fonctionnement en observant la redirection de «
/hello/bob/42
» dans la barre de développement de votre navigateur (pensez à cocher « Preserve log » pour pouvoir visualiser la réponse de redirection)
De la vue à la route ¶
Symfony
et son modèle de routeur prévoient de ne jamais écrire manuellement des URL
vers les ressources de l'application. Les URL
doivent être produites automatiquement en Twig
en se basant sur le nom de la route correspondant à l'URL
souhaitée.
Travail à réaliser
- Faites le ménage dans l'action «
index()
» qui avait été générée par le «MakerBundle
» dans le contrôleur «HelloController
» pour ne préserver que le rendu de la vue - Faites également le ménage dans le modèle associé «
hello/index.html.twig
» en supprimant le contenu de tous les blocs hormis le titre - Consultez les règles de production de liens vers des ressources de l'application
- Dans une liste à puces, créez un lien relatif permet de produire le résultat de «
5
» « Hello » à «Joe
»InformationPhpStorm
peut vous proposer les routes existantes dans les fonctionsTwig
«path()
» ou «url()
», profitez-en. - Dans une liste à puces, créez un lien absolu permet de produire le résultat de «
8
» « Hello » à «Bob
» - Vérifiez le bon fonctionnement de vos liens
- Vérifiez que vos deux liens sont bien respectivement relatif et absolu en observant le code
HTML
reçu par votre navigateur
Introduction aux tests fonctionnels ¶
Vous avez été sensibilisés aux tests pendant le « TP Développement d'une application Web de consultation et modification de morceaux de musique ». Vous avez certainement remarqué le répertoire « tests
» dans l'arborescence de Symfony
. Il contiendra les tests unitaires, d'intégration ou fonctionnels associés à vos classes.
Symfony
est nativement prévu pour fonctionner avec PHPUnit
grâce au « PHPUnit Bridge ». Pour des raisons de simplicité d'écriture et de facilité de compréhension, nous utiliserons plutôt Codeception
qui possède un module pour Symfony
.
Installation et configuration de Codeception
¶
Travail à réaliser
- Installez
Codeception
et ses modules «asserts
» et «symfony
» à l'aide deComposer
:composer require --dev --no-interaction codeception/codeception codeception/module-asserts codeception/module-symfony
InformationL'option «
--no-interaction
» permet d'éviter toute interaction lors de l'installation. Dans ce cas particulier, il ne vous sera donc pas demandé si vous souhaitez appliquer les recettesFlex
deCodeception
. Si toutefois la question vous est tout de même posée, nous ne souhaitons pas les appliquer car cela crée de nombreux répertoires et fichiers dans «tests
» et nous n'en avons pas l'utilité. - Initialisez
Codeception
pour votre projet :php vendor/bin/codecept bootstrap --namespace=App\\Tests --empty
- Ajoutez les paramètres de configuration de
Symfony
à l'environnement deCodeception
en complétant le fichier «codeception.yml
» :params: - .env - .env.test
- Modifiez la configuration de
PHP CS Fixer
dans le fichier «.php-cs-fixer.dist.php
» pour exclure le répertoire «tests/Support/_generated
»
Tests de l'action « manyTimes()
» du contrôleur « HelloController
»
¶
Travail à réaliser
- Créez une nouvelle suite de tests que nous dédirons aux contrôleurs :
php vendor/bin/codecept generate:suite Controller
- Mettez au propre le source
PHP
généré :composer fix:phpcs
InformationLes scripts
Composer
peuvent être lancés en omettant une partie de leur nom, tant qu'il n'y a pas d'ambiguïté. Ici, «composer fix:p
» est équivalent à «composer fix:phpcs
». - Créez un «
Cest
» («Codecept
» + «Test
») dédié à l'action «manyTimes()
» du «HelloController
» :php vendor/bin/codecept generate:cest Controller Hello\\ManyTimes
- Activez les modules «
Symfony
» et «Asserts
» de la suite «Controller
» en remplaçant la clé «modules
» dans le fichier «tests/Controller.suite.yml
» :modules: # enable helpers as array enabled: - Symfony: app_path: 'src' environment: 'test' - Asserts:
- Lancez la construction des classes «
Actor
» deCodeception
pour prendre en compte les nouveaux modules :php vendor/bin/codecept build
- Remplacez le fichier des tests de votre contrôleur par l'ensemble de tests «
ManyTimesCest.php
» (télécharger)InformationCes tests fournis sont assez détaillés pour que vous les utilisiez comme base d'exemple.
- Lancez les tests à l'aide de la commande
php vendor/bin/codecept run
- Vérifiez que votre code passe les tests
Ajout de scripts Composer
pour lancer les tests
¶
Travail à réaliser
- Ouvrez le fichier «
composer.json
» - Ajoutez un script «
test:codeception
» qui- nettoie le répertoire «
_output
» et le code généré parCodeception
- lance les tests de
Codeception
- nettoie le répertoire «
- Ajoutez un script «
test
» qui lance :- le script
Composer
qui teste la mise en forme du codePHP
- le script
Composer
qui teste la mise en forme du codeTwig
- le script
Composer
des tests avecCodeception
- le script
- Décrivez ces scripts
Composer
dans «composer.json
» et dans la documentation du projet
Configuration de l'accès base de données de l'application ¶
Vous avez utilisé l'outil « symfony
» en ligne de commande pour installer votre application Symfony
. Des valeurs par défaut ont été affectés à divers paramètres de configuration dans le fichier « .env
» situé à la racine de votre projet.
Remarque importante
Le fichier « .env
» sera inclus dans votre dépôt Git
. Il est donc vital qu'il ne contienne pas vos mots de passe ! Il servira uniquement de base à la configuration en mentionnant les paramètres à définir. La configuration effective de votre application locale se fera dans le fichier « .env.local
». Ce dernier est par défaut exclu du gestionnaire de versions et doit le rester.
Travail à réaliser
- Dans le fichier «
.env
», observez les exemples de configuration de la base de données à travers les valeurs de la variable «DATABASE_URL
»InformationLes lignes commençant par «
#
» sont des commentaires et ne sont pas prises en compte dans la configuration. Elles servent ici à donner des exemples de configuration. - Afin de faciliter le travail de correction de vos travaux, modifiez la ligne en commentaire de la variable «
DATABASE_URL
» dans le fichier «.env
» pour qu'elle corresponde à une base de donnéesMariaDB
version 10.2.25# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
devientDATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.2.25-MariaDB&charset=utf8mb4" # DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
- Copiez le fichier «
.env
» en «.env.local
» à partir dePhpStorm
et ouvrez-le - Donnez les valeurs adéquates à la variable
DATABASE_URL
afin de vous connecter sur le serveur dont le nomDNS
est «mysql
» accessible avec l'utilisateur «web
» dont le mot de passe est «web
» (ou selon vos paramètres si vous êtes sur votre ordinateur personnel) pour accéder à la base de donnéesMySQL
(MariaDB 10.2.25
) nommée «cutron01_contact_demo
»InformationRetrouvez les parties constitutives d'une
URL
dans le sujet de TP sur le proptocoleHTTP
que vous avez réalisé en première année.Si vous souhaitez travailler sur votre ordinateur personnel, vous pouvez télécharger le script de création de la base de données (Clic droit puis « Enregistrer sous… »).
- Vérifiez votre accès à la base de données :
bin/console doctrine:query:sql "SELECT * FROM contact WHERE id=4"
Vous devriez obtenir une ligne de résultat :
---- ----------- ---------- ------------------------------ id firstname lastname email ---- ----------- ---------- ------------------------------ 4 Christine Michaud christine.michaud@raynaud.fr ---- ----------- ---------- ------------------------------
Consultation des contacts ¶
Le modèle de données étant à présent configuré, il est temps de l'utiliser. Pour cela, vous allez construire une nouvelle fonctionnalité de listage de l'ensemble des contacts de la base de données.
Modèle de données ¶
Symfony
intègre Doctrine
pour effectuer la persistance et l'accès aux données dans une base de données. L'interaction entre votre application et la base de données se fera à travers des objets PHP
appelés des entités qui constituent la logique métier de l'application ainsi que les relations entre les entités. Par convention, les définitions de ces classes seront localisées dans le répertoire src/Entity
.
Afin que ces classes puissent interagir avec le SGBD, il convient de définir les liens entre les objets PHP
et la base de données. Cette opération est qualifiée de « mapping » qui est décrit sous forme d'attributs PHP
dans la définition de la classe. Le « mapping » peut être réalisé manuellement ou de façon interactive à l'aide du « MakerBundle
».
Vous avez configuré votre application pour accéder à la base de données « cutron01_contact_demo
» :
Information
La démarche habituelle pour un projet Symfony
est de créer les entités et leurs relations en fonction des besoins révélés par la phase d'analyse, puis de créer la base de données à l'aide de Doctrine
, pour ensuite insérer des données factices qui permettent d'obtenir une application de démonstration pour le développement. Nous dérogeons temporairement à cette règle pour des raisons de progression pédagogique en vous fournissant une base de données qui contient déjà des données générées par nos soins.
Travail à réaliser
- Lisez le paragraphe « Creating an Entity Class »
- Utilisez le «
MakerBundle
» pour générer l'entité «Contact
» conformément à la tableMySQL
:bin/console make:entity Contact
en donnant les propriétés suivantes :- «
firstname
» de type «string
» de taille «30
» non «nullable
» - «
lastname
» de type «string
» de taille «40
» non «nullable
» - «
email
» de type «string
» de taille «100
» non «nullable
»
- «
- Actualisez la liste des fichiers de votre répertoire «
Entity
» par un clic droit sur la racine du projet puis « 🗘 Reload from disk » - Observez le fichier généré dans le répertoire «
src/Entity
» - Consultez la classe générée et expliquez les divers attributs
PHP
. - Observez le fichier généré dans le répertoire «
src/Repository
»
Liste des contacts ¶
Travail à réaliser
- Créez un nouveau contrôleur «
ContactController
» à l'aide du «MakerBundle
» - Lisez le chapitre « Récupérer des objets dans la base de données » de la documentation de
Symfony
- Supprimez tous les éléments inutiles de l'action et de la vue ainsi que les paramètres passés à la vue
- En paramètre de l'action «
index()
» générée par défaut, demandez le service «ContactRepository
» - Dans l'action, utilisez la méthode «
findBy()
» de la classe «EntityRepository
» afin de récupérer l'ensemble des contacts de la base de données (critère de sélection vide) par ordre alphabétique (tri ascendant sur le nom puis le prénom) - Transmettez l'ensemble des contacts à la vue
Twig
- Donnez un titre au contenu Web produit et proposez un titre de niveau 1 représentatif
- Dans la vue, effectuez le parcours de l'ensemble des contacts pour produire une liste à puces présentant chaque contact sous la forme « nom, prénom »
- Vérifiez que la liste s'affiche correctement
Tests fonctionnels ¶
Afin de poursuivre les tests fonctionnels, vous allez mettre en place des contrôles sur le résultat de la liste des contacts.
Information
La démarche des tests liés à la consultation de la base de données est ici incomplète (voire incorrecte) puisque nous nous appuyons sur la base de données de développement. Ceci vous permet néanmoins de prévoir les tests et la démarche sera rectifiée par la suite.
Travail à réaliser
- Copiez votre fichier d'environnement de test «
.env.test
» en «.env.test.local
» - Définissez la variable contenant le chemin d'accès à la base de données comme dans l'environnement de développement
- Ajoutez ce nouveau fichier d'environnement dans le fichier de configuration de
Codeception
«codeception.yml
» - Mettez en commentaire la propriété «
dbname_suffix
» de «when@test.doctrine.dbal
» du fichier «config/packages/doctrine.yaml
» pour éviter d'ajouter «_test
» à la fin du nom de la base de données de test - Générez une classe de «
Cest
» pour l'action «index()
» du «ContactController
» :php vendor/bin/codecept generate:cest Controller Contact\\Index
- Sur le modèle des tests présentés en début de TP, vérifiez dans un même test que la ressource «
/contact
» donne une réponseHTTP
200
et que le corps de la réponse vérifie que :- le titre de la page Web contient « Liste des contacts »
- un titre de niveau 1 contenant « Liste des contacts » est présent
- une liste à puce contient 195 éléments
InformationProfitez des suggestions de
PhpStorm
au fil de la saisie de votre code pour découvrir les méthodes à utiliser.
Remarque importante
Codeception
enregistre dans le répertoire « tests/_output
» le corps de la réponse HTTP
des ressources impliquées dans des tests échoués. Consultez ces contenus, ceci facilite grandement la recherche des erreurs !
Détails d'un contact, version naïve ¶
Vous allez à présent rechercher un contact pour afficher le détail de ses données. Cette recherche va s'effectuer de façon naïve à partir de l'identifiant du contact. Ceci permet de comprendre la mécanique et d'arriver en douceur au « ParamConverter
» dans la partie suivante.
Travail à réaliser
- Dans votre contrôleur «
ContactController
», créez une action «show(int $contactId)
» qui reçoit en paramètre un identifiant de contact - Associez une route de chemin «
/contact/{contactId}
» à la nouvelle action - Appliquez les conditions requises à «
contactId
» - Dans la nouvelle action, demandez le rendu de la vue
Twig
«contact/show.html.twig
» - Générez le fichier
Twig
à l'aide dePhpStorm
- Vérifiez la bonne interaction de ces trois éléments
- En paramètre de l'action «
show()
», demandez le service «ContactRepository
» - Dans votre action, récupérez le contact concerné et transmettez-le à la vue
- Si le contact n'existe pas, levez une «
NotFoundHttpException
» dont le texte doit être explicite - Présentez le nom et le prénom du contact dans le titre du document
HTML
et dans un titre de niveau 1 - Présentez toutes les informations du contact (hormis l'identifiant) dans une liste de définitions
- Vérifiez que la route produit un résultat conforme
- Vérifiez que la route produit une réponse «
404
» pour un identifiant de contact qui n'existe pasInformationVous ajouterez des tests pour cette action dès que vous aurez des générateurs de données fictives.
Détails d'un contact, utilisation d'un « EntityValueResolver
»
¶
Vous allez à présent rechercher un contact en utilisant un « EntityValueResolver
» comme préconisé dans les bonnes pratiques. Cette démarche permet de simplifier le code.
Travail à réaliser
- Dans le contrôleur «
ContactController
», modifiez le chemin de la route «/contact/{contactId}
» pour que le nom du paramètre corresponde au nom de l'identifiant de l'entité «Contact
» - Modifiez les paramètres de l'action «
show()
» pour :- Remplacer l'identifiant de contact par un «
Contact
» - Supprimer le service «
ContactRepository
» qui sera inutile
- Remplacer l'identifiant de contact par un «
- Maintenez les conditions requises sur le paramètre du chemin de la route
- Supprimez le code maintenant inutile de l'action, en particulier le test
- Vérifiez que la route produit toujours un résultat conforme
- Vérifiez que la route produit une réponse «
404
» pour un identifiant de contact qui n'existe pas
Des routes pour produire des URL ¶
Vous venez de construire une liste de contacts et les détails d'un contact. Il convient à présent de rendre accessible les détails d'un contact par un clic sur son nom dans la liste des contacts. Vous n'écrirez pas d'URL
mais utiliserez les fonctionnalités Twig
permettant de les produire à votre place à partir du nom des routes.
Travail à réaliser
- Si nécessaire, relisez le chapitre « Linking to Pages » de la documentation
- Modifiez votre vue «
contact/index.html.twig
» afin qu'elle propose à présent des liens vers les détails du contact sur chaque nom de contact - Vérifiez que les liens sont correctement produits et fonctionnels
- Améliorez le précédent test fonctionnel pour qu'il vérifie que la liste à puces contient 195 liens
- Écrivez un nouveau test permettant de cliquer sur le premier lien de la liste des contacts et de vérifier que la route obtenue est la bonne (sans tenir compte du paramètre de route)
Mise en place de votre base de données, retour aux bonnes pratiques ¶
Dans la partie précédente, vous avez consulté une base de données existante. La démarche habituelle lors de la création d'un projet Symfony
est de construire de nouvelles entités liées entre elles par des relations, de gérer des versions des entités et de la base de données et de remplir la base de données avec des données, factices ou non, permettant de faire fonctionner le projet complet directement après clonage. Vous allez donc à présent suivre cette démarche et apprendre à construire des données factices générées aléatoirement à partir de « Faker
».
Création de la base de données ¶
Avant de pouvoir créer la base de données, vous allez devoir modifier la configuration pour accéder à votre propre base de données MySQL
.
Travail à réaliser
- Ouvrez le fichier de la configuration locale du projet
- Modifiez la configuration base de données pour :
- Utiliser votre compte
MySQL
en lieu et place de l'utilisateur «web
» - Modifiez la base de données utilisée en «
votre_login_contact
» - Mettez la valeur du paramètre «
serverVersion
» en accord avec le serveurMySQL
du département, à savoir «mariadb-10.2.25
»InformationLa valeur de «
serverVersion
» doit impérativement être correcte pour que les migrations fonctionnent. En effet,MySQL
etMariaDB
n'ont pas la même façon d'accéder aux méta-informations de la base de données.
- Utiliser votre compte
- Lancez la création de la base de données :
bin/console doctrine:database:create
- Sur
phpMyAdmin
, vérifiez que la base a bien été créée
Création d'une migration de données ¶
Rappelons que vous avez créé une entité « Contact
». Cette entité n'est plus persistante, maintenant que la base de données de démonstration a été débranchée. Vous allez donc reprendre le cours normal de la création du projet en effectuant avec Doctrine
la synchronisation entre les entités et la base de données pour que la structure de la base de données soit équivalente à celle de l'ensemble des entités.
Puisque vos entités peuvent évoluer au fil des fonctionnalités développées, il est normal que la base de données évolue de la même façon. Cependant, contrairement à la base de données de développement qui peut être détruite sans danger, la base de données de production doit pouvoir être modifiée sans perte ni corruption de données. C'est l'objectif des migrations qui vont contenir les requêtes permettant de faire évoluer la base de données.
Travail à réaliser
- Lancez la construction de la première migration
bin/console make:migration
- Observez le contenu de la migration générée dans le répertoire «
migration
»
Création du schéma de la base de données ¶
Les migrations contiennent l'ensemble des requêtes SQL
permettant d'appliquer et de supprimer la mise à jour de la base de données à un instant donné de l'évolution du projet. Doctrine maintient l'état des migrations appliquées sur la base de données dans une table « doctrine_migration_versions
». Ainsi, la comparaison de l'ensemble des fichiers contenus dans le répertoire « migrations
» et du contenu de la table « doctrine_migration_versions
» permet de connaître les migrations à appliquer pour que la structure de la base de données soit à jour.
Travail à réaliser
- Lancez l'exécution des migrations
bin/console doctrine:migrations:migrate
- Sur
phpMyAdmin
, vérifiez la présence, la structure et le contenu de la table «doctrine_migration_versions
» - Vérifiez la présence, la structure et le contenu de la table «
contact
» - Vérifiez que l'affichage de la liste des contacts fonctionne toujours, mais ne contient aucun contact
Générateur de données factices ¶
Pour faciliter la démarche de remplissage de la base de données de développement, vous utiliserez les bundles « DoctrineFixturesBundle
» et « ZenstruckFoundryBundle
». Le premier permet de gérer la création de données factices (« fixtures
») avec Doctrine
. Le second propose de faciliter le processus de création d'une entité en définissant les valeurs par défaut des propriétés d'une nouvelle instance, souvent générées aléatoirement avec « Faker
». Par ailleurs, les méthodes « create*()
» des « Factory
» génèrent de nouvelles instances mais les font aussi persister automatiquement en base de données.
Travail à réaliser
- Utilisez
Composer
pour demander le bundle «orm-fixtures
» pour le développement (ne pas oublier l'option «--dev
» !) - Observez les nouveaux fichiers à l'aide de l'onglet « Commit » de
PhpStorm
- Effectuez un «
commit
» avec ces fichiers - Utilisez
Composer
pour demander le bundle «zenstruck/foundry
» pour le développement - Observez les nouveaux fichiers à l'aide de l'onglet « Commit » de
PhpStorm
- Effectuez un «
commit
» avec ces fichiers - Parcourez la documentation du «
DoctrineFixturesBundle
» - Lancez la création d'un générateur de données pour les contacts :
bin/console make:fixtures ContactFixtures
- Observez le contenu du fichier généré et supprimez le corps de la méthode «
load()
» qui sera complété dans quelques instantsInformationLe code d'exemple qui est proposé lors de la génération du fichier s'applique lorsque l'on utilise directement
Doctrine
. Puisque nous utilisons «Foundry
» pour nous faciliter la tâche, il est hors de propos. - Effectuez un «
commit
» avec ce fichier - Lisez le chapitre «
Generate
» et de la documentation de «Foundry
» - Lancez la création d'une nouvelle forge de données :
bin/console make:factory
- Observez le fichier généré
- Effectuez un «
commit
» avec ce fichier - Modifiez la méthode «
defaults()
» pour que :- le nom soit un nom de famille généré par «
Faker
» - le prénom soit un prénom généré par «
Faker
» - l'email soit de la forme «
prenom.nom@domain
», dans lequel le prénom et le nom sont en minuscule, sans caractères accentués et le domaine est un nom de domaine généré par «Faker
»InformationUne translittération «
'Any-Latin; Latin-ASCII'
» permet de supprimer tous les accents en remplaçant chaque caractère accentué par un caractère équivalent non accentué.Une translittération «
'Any-Lower; Latin-ASCII'
» permet de supprimer tous les accents en remplaçant chaque caractère accentué par un caractère minuscule équivalent non accentué.
- le nom soit un nom de famille généré par «
- Puisque certains noms de famille et certains prénoms composés contiennent des espaces incompatibles avec la structure d'une adresse email, il est plus sûr de remplacer tout caractère non alphabétique par un tiret
- Vous devez effectuer des traitements identiques sur le nom et sur le prénom de l'utilisateur en vue de construire son mail, proposez une méthode protégée «
normalizeName
» qui effectue ces traitements et qui sera utilisée par «defaults()
» - Votre méthode construit un «
Transliterator
» à chaque appel, ce qui est inutile et inefficace : construisez le «Transliterator
» dans le constructeur de la classe, stockez-le dans une propriété d'instance et utilisez-le dans «normalizeName()
» - Effectuez un «
commit
» avec ce fichier - Lisez les exemples du paragraphe « Using your Factory » de la documentation de «
Foundry
» - Modifiez le générateur de données de «
Contact
» pour qu'il crée 150 «Contact
» en utilisant la forge précédemment construiteInformationVous avez certainement envie de générer les données factices mais il va falloir attendre un peu !
Génération de la base de données et des données factices ¶
A ce stade, vous pourriez générer les données factices en base de données. Malheureusement, si vous avez commis des erreurs dans votre code et tentez de générer un nouveau jeu de données corrigé, elles vont se cumuler avec les anciennes. Pour palier ce problème, vous allez créer un script Composer
qui reprend toutes les étapes précédentes pour générer de façon fiable une nouvelle base de données contenant les données factices.
Travail à réaliser
- Créez un script
Composer
«db
» qui exécute les étapes suivantes :- Destruction forcée de la base de données
php bin/console doctrine:database:drop --force --if-exists
- Création de la base de données
php bin/console doctrine:database:create
- Application des migrations successives sans questions interactives
php bin/console doctrine:migrations:migrate --no-interaction
- Génération des données factices sans questions interactives
php bin/console doctrine:fixtures:load --no-interaction
- Destruction forcée de la base de données
- Documentez ce script dans le fichier «
composer.json
» - Documentez ce script dans le fichier «
README.md
» - Puisque cela n'a pas été fait auparavant, documentez la configuration de la base de données dans «
README.md
» - Lancez le script
Composer
«db
» - Observez le contenu de la table «
contact
» dansphpMyAdmin
- Réalisez le même type d'observation en utilisant la console, par exemple avec :
bin/console doctrine:query:sql "SELECT * FROM contact LIMIT 5"
InformationIl est essentiel de vérifier la pertinence de toutes les données générées. Il faut vérifier toutes les lignes et toutes les colonnes pour avoir une vision globale de la cohérence de ce qui est produit, aléatoirement ou pas.
- En vous inspirant de la documentation de «
Faker
» dans «Foundry
», modifiez la configuration de «Faker
» qui qu'il génère des textes en français - Réinitialisez la base de données et contrôlez les données produites qui doivent ressembler à :
bin/console doctrine:query:sql "SELECT * FROM contact LIMIT 5" ---- ----------- ----------- ------------------------------ id firstname lastname email ---- ----------- ----------- ------------------------------ 1 Édith Gregoire edith.gregoire@perez.com 2 Adélaïde Gauthier adelaide.gauthier@dupre.fr 3 Maurice Boulanger maurice.boulanger@allard.net 4 Louis Caron louis.caron@marty.org 5 Paulette Besnard paulette.besnard@rey.org ---- ----------- ----------- ------------------------------
- Vérifiez l'affichage de ces nouvelles données dans votre application
Tests fonctionnels basés sur des données factices ¶
Les tests fonctionnels dépendant de données ont précédemment été réalisés sur une version de démonstration de la base de données de développement. Ce principe est problématique puisqu'il nécessite un serveur de base de données MySQL
contenant des données et une connaissance du jeu de données. Habituellement, des données factices sont générées à la volée pour chaque test sur un moteur SQLite
qui ne nécessite pas d'infrastructure particulière.
Vous allez donc utiliser « Foundry
» pour générer des données factices lors des tests. Il faut installer le module « Doctrine
» de Codeception
pour pouvoir tester le contenu dans la base de données.
Travail à réaliser
- Ajoutez à votre projet la dépendance de développement au module «
codeception/module-doctrine2
»composer require --dev codeception/module-doctrine
- Activez le module «
Doctrine
» dans la configuration de la suite des tests «Controller
» (fichier «tests/Controller.suite.yml
») - Activez l'option «
cleanup
» pour que chaque test s'exécute dans une transaction, empêchant ainsi les effets de bords - Ajoutez le fichier source de cet utilitaire («
Helper
») à votre projet en le plaçant dans le répertoire «tests/Support/Helper
» - Activez le «
Helper
» qui dépend du module «Symfony
» en ajoutant les lignes suivantes à la liste des modules activés dans le fichier «tests/Controller.suite.yml
» :- \App\Tests\Support\Helper\EntityManagerReset: depends: Symfony
InformationCe «
Helper
» permet de réinitialiser l'EntityManager
entre chaque test pour éviter les effets de bords. - Générez les classes «
Actor
» deCodeception
avec le nouveau module activé :php vendor/bin/codecept build
- Configurez l'accès à une base de données
SQLite
de test dans «.env.test
» en prenant tel quel le modèle proposé dans «.env
»InformationIl est classique de configurer une base de données
SQLite
pour les tests directement dans le fichier «.env.test
». - Supprimez le fichier «
.env.test.local
»rm .env.test.local
- Supprimez la référence à «
.env.test.local
» dans «codeception.yml
» - Modifiez le script
Composer
«test:codeception
» pour qu'il initialise la base de données de test avant de lancer les tests :- Nettoyage du répertoire «
_output
» et du code généré parCodeception
php vendor/bin/codecept clean
- Destruction silencieuse forcée de la base de données
php bin/console doctrine:database:drop --force --quiet --env=test
- Création silencieuse de la base de données
php bin/console doctrine:database:create --quiet --env=test
- Création silencieuse du schéma de la base de données
php bin/console doctrine:schema:create --quiet --env=test
- Exécution des tests
Codeception
php vendor/bin/codecept run
- Nettoyage du répertoire «
- Documentez ce script dans le fichier «
composer.json
» - Documentez ce script dans le fichier «
README.md
» - Lancez le script
Composer
«test:codeception
» et constatez qu'il produit des erreurs - Mettez à jour le test de la liste des contacts en le débutant par la création de 5 «
Contact
» avec «ContactFactory
» - Ajoutez un test de clic sur le premier contact de la liste :
- À l'aide de «
ContactFactory
», créez un «Contact
» «Joe Aaaaaaaaaaaaaaa
» - Toujours à l'aide de «
ContactFactory
», créez 5 «Contact
» - Cliquez sur le lien «
Aaaaaaaaaaaaaaa, Joe
» - Vérifiez que la réponse est un succès
- Vérifiez que vous êtes sur la route «
app_contact_show
» avec les bonnes valeurs de paramètres
- À l'aide de «
- Ajoutez un test qui contrôle que les contacts sont correctement triés :
- Avec «
ContactFactory
», créez une séquence d'insertion de 5 «Contact
» dont deux ont le même nom de familleInformationPour que votre test soit pertinent, utilisez des contacts dont les noms et prénoms sont fixés et insérés dans un ordre différent de l'ordre alphabétique attendu.
- Utilisez la méthode «
grabMultiple()
» du module «WebDriver
» deCodeception
pour récupérer la liste des contacts - Comparez l'ordre attendu et l'ordre constaté
- Avec «
- Vérifiez que votre code passe tous les tests
Structuration de l'application ¶
Votre « application » n'en a pour l'instant aucune des caractéristiques classiques. Vous allez utiliser Bootstrap
pour vous permettre d'ajouter facilement une barre de navigation et d'autres éléments par la suite. Nous nous attacherons peu au design de l'application pour privilégier les fonctionnalités apportées par votre code et les composants Bootstrap
.
Ajout d'une page d'accueil ¶
Votre application sert actuellement les ressources « /contact
» et « /contact/{id}
» mais pas « /
» qui donne une réponse « 404
». Vous allez ajouter un nouveau contrôleur dont l'unique rôle sera de rediriger de « /
» vers « /contact
».
Travail à réaliser
- Créez un nouveau contrôleur «
HomeController
» - Modifiez la route créée par défaut pour qu'elle soit associée au chemin «
/
» - Remplacez le code de l'action pour effectuer une redirection «
303
» vers la route associée à «/contact
» - Supprimez le fichier de la vue et le répertoire «
templates/home
» qui deviennent inutiles
Intégration de Bootstrap
¶
La première étape va consister à intégrer Bootstrap
à votre application en utilisant les contenus fournis par un « CDN
».
Travail à réaliser
- Lisez la documentation sur le démarrage rapide
Bootstrap 5
- Modifiez votre « template » de base pour y intégrer le
CSS
et leJavaScript
deBootstrap 5
dans les blocs dédiés - Profitez-en pour définir le «
viewport
» adapté - Ajoutez la langue du document
HTML
Ajout d'une barre de navigation ¶
Votre application sera plus facile à utiliser avec une barre de navigation contenant les actions classiques que vous proposerez.
Travail à réaliser
- Découvrez comment fonctionne la «
Navbar
»Bootstrap
en lisant la documentation - Copiez l'exemple de la documentation dans votre modèle
Twig
de base - Conservez les éléments suivants :
- le lien «
navbar-brand
» qui contient le texte «Contacts
» et mène à la page d'accueil - l'élément «
navbar-collapse
» qui correspond au menu complet ou au menu hamburger selon la taille dela fenêtre - une liste «
navbar-nav
» qui ne contiendra que l'élément «nav-item
» le plus simple - le formulaire de recherche que vous franciserez
- le lien «
- Vérifiez que la barre de navigation s'affiche correctement, notamment sous forme d'un menu burger en mode mobile
Gestion des « assets », feuille de style de l'application ¶
Les contacts sont actuellement listés de façon simple, sans structure du nom et du prénom. Ceci rend impossible toute mise en forme particulière. Vous allez découvrir comment utiliser une feuille de style sous forme d'« asset », puis vous ajouterez de la structure dans la liste des contacts.
Travail à réaliser
- Lisez le chapitre « Mapping and Referencing Assets »
- Observez la feuille de style «
assets/styles/app.css
» de votre projet - Cherchez l'utilisation de la fonction Twig «
asset()
» dans le « template » de base - Observez le code
HTML
correspondant dans le code source de la page reçu par le navigateur - Lisez le chapitre « Serving Assets in dev vs prod » Information
En mode développement, l'exécutable «
symfony
» sert automatiquement les « assets », bien qu'ils ne soient pas présents dans la racine de l'espace web de votre application (répertoire «public
»). En mode production, les fichiers sont servis par un serveur web et doivent donc être générés par une commande de la console au moment du déploiement de l'application. - Modifiez la couleur de fond dans la feuille de style
- Modifiez la vue de listage des contacts pour ajouter un «
span
» autour du nom et un autour du prénom, auxquels vous associerez respectivement les classesCSS
«firstname
» et «lastname
» - Complétez la feuille de style pour que la classe «
lastname
» soit en gras et en petites majuscules
Mise à jour des tests ¶
La barre de navigation contient une liste à puces. Sa présence peut fausser le résultat du sélecteur « ul > li
» présent dans les tests car celui-ci est trop peu restrictif. La solution va consister à rendre le système plus testable en augmentant la spécificité de la liste des contacts et en modifiant le sélecteur dans les tests.
Travail à réaliser
- Vérifiez que les tests ne passent plus
- Ajoutez la classe
CSS
«contacts
» à la liste des contacts - Mettez le sélecteur des tests en accord avec cette modification
- Vérifiez que les tests passent de nouveau
Recherche des contacts ¶
La barre de navigation que vous venez d'ajouter comporte un champ de recherche qui doit être rendu fonctionnel. Pour cela, il faut intervenir sur trois aspects : le modèle avec une nouvelle méthode dans le « Repository
», le contrôleur pour déclencher la recherche avec le texte reçu et la vue pour proposer l'interface de saisie et afficher le résultat. Vous chercherez donc le texte fourni par l'utilisateur dans le nom et le prénom des contacts.
Recherche dans le modèle ¶
La première étape va consister à rechercher les données au niveau du modèle, donc dans le « ContactRepository
». Un contact est retenu si son nom ou son prénom contient le texte cherché.
Travail à réaliser
- Créez une nouvelle méthode «
search()
» dans le «ContactRepository
» - Ajoutez-lui un paramètre représentant le texte cherché ainsi qu'un type de retour «
array
» - Ajoutez une annotation pour préciser le type de retour «
Contact[]
» - Lisez la partie «
Querying with the Query Builder
» la documentation deDoctrine
et découvrez les possibilités offertes par le «QueryBuilder
» - Utilisez le «
QueryBuilder
» pour effectuer l'interrogation des données donnant les contacts, triés par nom puis prénom, dont le nom ou le prénom contient le texte cherché
Logique dans le contrôleur ¶
La méthode « index()
» contrôleur « ContactController
» doit être adapté à la recherche.
Travail à réaliser
- Ouvrez le fichier du «
ContactController
» et placez-vous au niveau de la méthode «index()
» - Remplacez l'utilisation de la méthode «
findBy()
» du «ContactRepository
» par la nouvelle méthode «search()
» - Vérifiez que l'affichage fonctionne toujours
- Demandez l'objet «
Request
» (correspondant à la requêteHTTP
) en le déclarant dans les paramètres de la méthode - Lisez comment utiliser l'objet «
Request
» - Récupérez la valeur du paramètre
HTTP
GET
«search
» ou une chaîne vide par défaut - Utilisez la valeur précédente comme texte de recherche des contacts
- Ajoutez le paramètre
HTTP
GET
«search
» dans l'URL
de listage des contacts (avec comme valeur «le
», par exemple) - Vérifiez que l'affichage est bien restreint à des contacts dont le nom ou le prénom contient «
le
»
Présentation dans la vue ¶
L'interface de saisie du texte cherché est présente mais elle n'est pas reliée à la recherche effective dans le contrôleur. Il faut mettre la saisie en accord avec le code du contrôleur et peaufiner l'interface utilisateur.
Travail à réaliser
- Fixez le nom du champ de saisie pour qu'il corresponde au code du contrôleur
- Testez le champ et le bouton de recherche
- Consultez l'
URL
«/contact/12
» puis utilisez le champ de recherche depuis cetteURL
- Ajoutez une action au formulaire pour que la route «
app_contact
» soit utilisée - Constatez que la valeur cherchée n'apparaît pas dans le champ de recherche lors de l'affichage des résultats
- Dans le contrôleur, passez la valeur de «
search
» à la vue - Donnez la valeur de «
search
», ou par défaut une chaîne vide, comme valeur du champ de recherche - Vérifiez le comportement de l'ensemble
Test de la fonctionnalité ¶
La pérennité de cette fonctionnalité passe nécessairement par un test.
Travail à réaliser
- Écrivez une nouvelle méthode de tests «
search()
» dans «tests/Controller/Contact/IndexCest.php
» - Utilisez le «
ContactFactory
» pour ajouter 4 contacts dont un contiendra une séquence de caractère dans le nom et un autre la même séquence dans le prénom - Lancez la recherche de la séquence de caractères
- Contrôlez les contacts retenus
- Vérifiez que le test passe
Optimisation et bonnes pratiques ¶
Le code que vous venez de produire est fonctionnel et testé. Les tests offrent la possibilité de modifier le code tout en veillant au maintien de la fonctionnalité. Vous allez donc améliorer le code en appliquant des bonnes pratiques.
Travail à réaliser
- Dans la méthode «
search()
» du «ContactRepository
», effectuez une recherche uniquement si le texte cherché n'est pas vide - Dans le contrôleur, remplacez l'utilisation de l'objet «
Request
» par l'utilisation de l'attributPHP
«MapQueryParameter
» pour obtenir la valeur du paramètre «search
» - Vérifiez que votre code passe toujours les tests
Catégories de contacts ¶
La structure des données persistantes est actuellement simpliste. Vous allez ajouter une entité pour les catégories afin qu'un contact appartienne possiblement à une catégorie.
Entité « Category
»
¶
Vous allez reproduire les opérations de construction d'une entité en vous aidant du « MakerBundle
».
Travail à réaliser
- Utilisez le «
MakerBundle
» pour créer une nouvelle entité «Category
» qui comporte une propriété «name
» de type «string
» avec une taille maximale de «30
» et qui ne peut pas être «null
» en base de données - Vérifiez le code source de la classe générée
Relations « Contact
» / « Category
»
¶
En même temps que la création de l'association entre « Contact
» et « Category
», vous ajouterez une propriété « phone
» à l'entité « Contact
».
Travail à réaliser
- Utilisez le «
MakerBundle
» pour modifier l'entité «Contact
» existante :bin/console make:entity Contact
- Ajoutez la propriété «
phone
» de type «string
» avec une taille maximale de «20
» et qui ne peut pas être «null
» en base de données - Ajoutez une propriété «
category
» de type relation «ManyToOne
» qui peut être nulle et qui impliquera une propriété «contacts
» dans l'entité «Category
» - Observez le code de «
Category
» ainsi que les modifications du code de «Contact
»
Migration ¶
Vous savez que toute modification de la structure de la base de données doit être référencée dans une migration.
Travail à réaliser
- Créez une nouvelle migration
- Vérifiez le code de la migration
- Constatez que l'affichage de la liste des contacts ne fonctionne plus
- Appliquez la migration
- Constatez que l'affichage de la liste des contacts fonctionne à nouveau
Données factices ¶
Les générateurs de données (« *Fixtures
») et les forges de données (« *Factory
») vont devoir être adaptés aux nouvelles entités.
Travail à réaliser
- Tentez de re-générer la base de données en utilisant votre script «
Composer
» - Mettez à jour la forge de «
Contact
» pour corriger le problème - Vérifiez que vos contacts ont des numéros de téléphone français valides
- Générez la forge pour la classe «
Category
» - Modifiez la forge pour que le nom de la catégorie soit généré aléatoirement comme un mot dont la première lettre est en majuscules
- Créez un générateur de données factices pour la classe «
Category
» - Téléchargez dans le répertoire «
data
» dans le répertoire «DataFixtures
»«Category.json
» (télécharger) qui contient une série de noms de catégories - Dans le générateur, utilisez les fonctions «
file_get_contents()
» et «json_decode()
» pour lire ce fichier et créer les instances de «Category
» correspondantes à l'aide de «CategoryFactory
»InformationVous devez faire référence au fichier «
Category.json
» depuis le fichier «CategoryFixtures.php
». Vous ne pouvez pas utiliser le répertoire courant «.
» qui fait référence au programme en cours d'exécution. Vous devez utiliser la constante magique qui donne le répertoire du fichier dans lequel elle est utilisée. - Générez une nouvelle version de la base de données et constatez l'apparition des catégories
- Indiquez les dépendances entre «
ContactFixtures
» et «CategoryFixtures
» - Modifiez le générateur de «
Contact
» pour qu'il affecte une catégorie aléatoire à 90% des contacts générésInformationLa méthode «
boolean()
» deFaker
admet un paramètre de type «integer
» qui fixe la probabilité d'obtenir «true
» sur 100.Puisque le tirage aléatoire de catégorie doit avoir lieu pour chaque entité «
Contact
» générée, vous allez devoir utiliser une fonction anonyme. Vous pouvez vous appuyer sur l'exemple fourni dans « Using with DoctrineFixturesBundle ». - Exécutez la commande suivante pour connaître le nombre de contacts dans chaque catégorie (dont la catégorie «
null
») :bin/console doctrine:query:sql "SELECT category.name AS category, COUNT(contact.id) AS contacts \ FROM contact \ LEFT JOIN category ON contact.category_id = category.id \ GROUP BY category.name \ ORDER BY category.name"
- Utilisez le générateur «
AppFixtures
» pour générer une catégorie aléatoire qui ne contiendra aucun contact - Indiquez les dépendances entre «
AppFixtures
» d'une part et «ContactFixtures
» et «CategoryFixtures
» d'autre partInformationVous devez imposer les dépendances pour que la catégorie construite dans «
AppFixtures
» reste vide. Elle doit donc être construite en dernier. - Exécutez la commande suivante pour connaître le nombre de contacts dans chaque catégorie (dont la catégorie vide) :
bin/console doctrine:query:sql "SELECT category.name AS category, COUNT(contact.id) AS contacts \ FROM contact \ RIGHT JOIN category ON contact.category_id = category.id \ GROUP BY category.name \ ORDER BY category.name"
Mise à niveau de l'affichage ¶
Le modèle ayant changé, les vues doivent être adaptées.
Travail à réaliser
- Modifiez la vue des détails d'un contact pour afficher le numéro de téléphone et le nom de la catégorie Information
Puisque la catégorie peut ne pas être définie, vous devez tester dans la vue si «
votre_contact.category
» est non nul pour ensuite accéder à «votre_contact.category.name
». L'opérateur «??
» vous facilite la tâche en vous autorisant l'accès à une valeur non définie sans erreur avec une valeur par défaut dans le cas où la valeur n'est pas définie.Trouvez facilement un contact dont la catégorie est vide en utilisant :
bin/console doctrine:query:sql "SELECT * FROM contact WHERE category_id IS NULL LIMIT 1"
- Créez un contrôleur «
CategoryController
» pour les catégories - Utilisez l'action générée par défaut pour effectuer un listage alphabétique des catégories mis en forme avec
Bootstrap
- Dans la «
Navbar
», ajoutez un lien «Catégories
» qui mène à la liste des catégories
Inclusion de « template » pour uniformiser et factoriser ¶
Vous devez vous attendre à construire une liste de contacts par catégorie. Vous avez déjà réalisé la liste de l'ensemble des contacts qui peut être restreinte par une recherche. Le code Twig
va donc être le même pour la liste de contacts d'une catégorie. Il serait particulièrement malvenu de dupliquer le code ! Vous allez donc extraire le code d'affichage de la liste des contacts et l'utiliser à deux endroits différents en l'incluant.
Travail à réaliser
- Créez un « template » «
templates/contact/_contacts.html.twig
» qui contient uniquement le codeHTML
d'affichage d'une liste de contacts extrait de la vue «templates/contact/index.html.twig
» - Modifiez la vue «
templates/contact/index.html.twig
» pour inclure ce « template » - Vérifiez l'affichage de la liste et lancez les tests
- Dans le contrôleur «
CategoryController
», créez une nouvelle action «show()
» qui liste les contacts d'une catégorie :- Utilisez un «
EntityValueResolver
» - Passez uniquement l'entité «
Category
» à la vue «templates/category/show.html.twig
» - Utilisez le « template » «
templates/contact/_contacts.html.twig
»
InformationPensez à passer une variable «
contacts
» en paramètre de votre inclusion de « template ». - Utilisez un «
Derniers détails ¶
Il reste quelques détails d'affichage à régler.
Travail à réaliser
- Consultez la ressource «
/category/1
» pour vérifier votre travail - Constatez que les contacts d'une catégorie ne sont pas triés
- Ajoutez un critère de tri sur la collection des contacts de la catégorie (nom puis prénom)
- Dans la liste de catégories, ajoutez un lien vers les contacts de la catégorie
Requêtes personnalisées avec le « QueryBuilder
»
¶
Jusqu'à présent, vous avez utilisé des méthodes de haut niveau pour effectuer des sélections dans la base de données. Votre code est resté éloigné des requêtes SQL
même si vous en avez retrouvé la philosophie dans la recherche des contacts par nom et/ou prénom. Dans cette partie, vous allez écrire des requêtes plus complexes et plus efficaces en utilisant le « QueryBuilder
».
Listage des catégories avec le nombre de contacts qu'elles contiennent ¶
Pour obtenir les catégories ainsi que le nombre de contacts qu'elles contiennent, votre requête doit sélectionner les objets « Category
» et l'entier donnant le nombre de contacts dans la même requête, pour des raisons évidentes d'efficacité. Vous devez donc créer une nouvelle méthode dans le « CategoryRepository
».
Travail à réaliser
- Dans l'action «
index()
» du «CategoryController
», modifiez l'utilisation du «CategoryRepository
» pour, dans un premier temps, sélectionnez l'ensemble des catégories triées par ordre alphabétique, en construisant une requête avec le «QueryBuilder
» - Vérifiez que vous pouvez toujours afficher la liste des catégories
- Effectuez une jointure externe sur les contacts avec «
leftJoin()
» - Groupez par catégorie en utilisant «
groupBy()
» - Vérifiez que votre vue fonctionne toujours
- Sélectionnez le total des contacts par catégorie avec «
addSelect('COUNT(alias_utilisé_dans_leftjoin) as count')
» - Constatez que votre vue ne fonctionne plus
- Dans le contrôleur, utilisez la fonction «
dump()
» pour pouvoir explorer le contenu de la collection dans la «WDT
» :InformationVous pouvez obtenir le même résultat en utilisant la fonction «
dump()
» deTwig
dans une structure «{% %}
». - Observez les clés du tableau contenu dans la première case de la collection permettant d'accéder aux valeurs sélectionnées : un entier pour l'entité «
Category
» et la chaîne «count
» pour le nombre de contacts - Renommez la sélection de l'entité «
Category
» avec «select('c as category')
» - Observez de nouveau la collection dans la «
WDT
» : pour chaque ligne, l'entité Category est accessible par la clé «category
» et le décompte des contacts par la clé «count
» - Ajustez le code de la vue pour afficher correctement les catégories et leur nombre de contacts dans un badge
Bootstrap
InformationPrenez soin de renommer les variables
Twig
qui portent les entités «Category
» et le décompte du nombre de contacts.Pensez à supprimer l'appel à la fonction «
dump()
» . - Créez une méthode «
findAllOrderedByNameWithContactCount()
» dans le «CategoryRepository
» pour y déporter la requête que vous avez élaborée
Conséquence du chargement paresseux (« lazy loading ») de l'ORM
¶
Lors de l'affichage des détails d'un contact, vous affichez ses informations propres ainsi que le nom de la catégorie du contact qui est issue de la relation avec l'entité « Category
». Le chargement paresseux (« lazy loading ») implique donc que la demande de la catégorie va engendrer une nouvelle requête. Afin d'optimiser les accès base de données, vous allez charger l'entité « Category
» au même moment que le chargement de l'entité « Contact
».
Travail à réaliser
- Consultez la fiche d'un contact associé à une catégorie
- Constatez à l'aide de la «
WDT
» que deux requêtes base de données sont nécessaires à l'affichage de la page : une pour récupérer l'entité «Contact
», l'autre pour récupérer l'entité «Category
» associée - Créez une méthode «
findWithCategory(int $id): ?Contact
» dans le «ContactRepository
» - Utilisez le «
QueryBuilder
» pour effectuer la requête de sélection du contact dont l'identifiant est demandé ainsi que de la catégorie associée :- Effectuez la jointure externe sur l'entité «
Category
» avec «leftJoin()
» - Sélectionnez l'entité «
Category
» à l'aide de «addSelect()
» - Ajoutez la restriction sur l'identifiant de l'entité «
Contact
» concernée avec «where()
» - Fixez la valeur de l'identifiant de l'entité «
Contact
» avec «setParameter()
»
- Effectuez la jointure externe sur l'entité «
- Lisez la documentation « Automatically Fetching Objects (EntityValueResolver) », en particulier le point sur l'utilisation d'une expression avec l'attribut
PHP
«MapEntity
» - Associez l'attribut
PHP
«MapEntity
» au paramètre «Contact
» dans l'action du contrôleur pour utiliser la méthode «findWithCategory()
» du «ContactRepository
» lors de la conversion du paramètre - Vérifiez qu'une seule requête base de données est effectuée pour l'affichage des détails du contact
Autre conséquence du chargement paresseux (« lazy loading ») de l'ORM
¶
Le même problème de nombre de requêtes trop important va survenir lorsque vous allez afficher la catégorie de chaque contact dans la liste des contacts.
Travail à réaliser
- Consultez la liste des contacts
- Ajoutez la catégorie des contacts sous forme d'un badge
Bootstrap
dans «templates/contact/_contacts.html.twig
» - Faîtes en sorte qu'un clic sur le badge conduise à la liste des contacts de la catégorie Information
Utilisez la classe
CSS
Bootstrap
«nav-link
» pour le lien contenu dans le badge. - Testez en
Twig
la présence de la catégorie du contact en utilisant «is not null
» - Constatez à l'aide de la «
WDT
» que sept requêtes base de données sont nécessaires à l'affichage de la page :- une pour récupérer l'ensemble des entités «
Contact
» - six qui sont la même requête avec un paramètre différent pour récupérer chacune des entités «
Category
» associées
- une pour récupérer l'ensemble des entités «
- Observez le temps d'exécution global et celui de l'ensemble des requêtes (rafraichissez la page plusieurs fois pour estimer des valeurs moyennes)
- Modifiez la méthode «
search()
» dans le «ContactRepository
» pour effectuer la jointure externe sur l'entité «Category
» et l'ajouter à la sélection - Constatez à l'aide de la «
WDT
» qu'une seule requête base de données est désormais nécessaires à l'affichage de la page - Observez le temps d'exécution global et celui de l'ensemble des requêtes (rafraichissez la page plusieurs fois pour estimer des valeurs moyennes) et constatez l'amélioration
- Consultez la liste des contacts d'une catégorie et constatez que l'affichage de la catégorie pour chaque contact est inutile
- Dans «
templates/contact/_contacts.html.twig
», combinez le test «show_category??true and
» pour vérifier s'il faut afficher la catégorie - Passez la valeur «
show_category: false
» lors de l'inclusion dans «templates/category/show.html.twig
» - Vérifiez le bon fonctionnement de l'ensemble
- Vérifiez que les tests passent toujours
Utilisation de formulaires ¶
Vous allez à présent proposer une interface de modification du contenu de la base de données. Pour cela, vous utiliserez des formulaires. Ces derniers seront construits sur un objet donné dont ils permettront la présentation sous forme de champs de saisie mais contiendront également les fonctionnalités de contrôle de cohérence.
Vous allez donc rendre possible l'édition d'objets « Contact
» en proposant de les modifier, de les créer puis de les supprimer.
Préparation du contrôleur ¶
Les chemins associés aux actions du CRUD
seront :
- «
/contact/create
» pour la création (Create) - «
/contact/{id}
» pour la lecture (Read) - «
/contact/{id}/update
» pour l'édition (Update) - «
/contact/{id}/delete
» pour la suppression (Delete)
Vous avez déjà réalisé l'action de lecture. Il vous reste à créer les actions associées à la création, l'édition et la suppression. Vous allez donc ajouter les actions nécessaires au contrôleur « ContactController
».
Travail à réaliser
- Ajoutez trois actions à votre contrôleur :
- «
update
» qui demande un paramètre de type entité «Contact
» - «
create
» sans paramètre - «
delete
» qui demande un paramètre de type entité «Contact
»
- «
- Ajoutez une condition requise sur «
id
» dans les routes conduisant à une conversion de paramètre pour qu'il prenne la forme d'un nombre - Créez les « templates » associés aux précédentes actions dans lesquels vous afficherez dans le titre de la page et dans un titre de niveau 1 :
- « Édition de Nom, prénom »
- « Suppression de Nom, prénom »
- « Création d'un nouveau contact »
InformationPuisque vous devez mettre la même valeur dans le titre de la page et dans le titre de niveau 1, il est judicieux de ne pas dupliquer les opérations. Vous êtes donc invité à utiliser une variable dans laquelle vous pouvez stocker une valeur construite en utilisant l'interpolation de variable
Twig
. - Vérifiez que les nouvelles routes sont bien prises en compte :
bin/console debug:router --show-controllers --env=prod
- Pour valider votre travail, ajoutez les « Cests » suivants à la suite de tests «
Controller\\Contact
» :
Interface de modification d'un « Contact
» existant
¶
Avant de généraliser l'utilisation d'un formulaire pour la création et l'édition d'un « Contact
», vous allez vous concentrer sur l'édition dans l'action « update()
».
Travail à réaliser
- Générez la classe de formulaire «
ContactType
» associée à l'entité «Contact
» :bin/console make:form ContactType Contact
- Consultez le fichier généré
- Construisez un formulaire «
ContactType
» dont les données proviennent de l'entité «Contact
» que vous récupérez en paramètre de votre action - Transmettez le formulaire à votre « template »
Twig
et affichez-leInformationNotez que, depuis
Symfony 6.2
, le formulaire peut être transmis en l'état à la vue car la méthode «$form->createView()
» sera appelée automatiquement. - Essayez votre formulaire et constatez le contenu de la liste déroulante de la catégorie
- Modifiez la définition du champ «
category
» en précisant la propriété de l'entité «Category
» qui doit être utilisée pour présenter les données à l'utilisateur - Essayez de nouveau votre formulaire et que le contenu de la liste déroulante n'est pas trié par ordre alphabétique
- Lisez le chapitre « Using a Custom Query for the Entities » de la documentation de «
EntityType Field
» - Modifiez votre champ de formulaire «
category
» pour qu'il récupère les données triées :… ->add('category', EntityType::class, [ 'class' => Category::class, 'choice_label' => 'name', 'query_builder' => function (EntityRepository $entityRepository) { return $entityRepository->createQueryBuilder('c') ->orderBy('c.name', 'ASC'); }, ]) …
- Essayez de nouveau votre formulaire et constatez qu'il fonctionne et que le contenu de la liste déroulante est trié par ordre alphabétique, mais qu'il n'est pas possible de sélectionner la catégorie «
null
»InformationCe défaut majeur sera corrigé plus tard.
- Lisez le chapitre sur les bonnes pratiques concernant les boutons d'envoi de formulaires (une description plus détaillée est présente dans une version plus ancienne de la documentation)
- Remplacez le rendu
Twig
de votre formulaire avec «form()
» par une décomposition du formulaire utilisant «form_start()
», un seul appel de «form_widget()
» pour les champs et «form_end()
», comme préconisé - Ajoutez un bouton d'envoi ayant pour texte « Modifier », en respectant les bonnes pratiques
- Profitez de l'occasion pour ajouter la classe «
container-lg
» à «<body>
» et étirer le bouton sur toute la largeur
Présentation des formulaires ¶
Le formulaire précédemment produit est fonctionnel mais visuellement minimaliste. Twig
peut rapidement vous apporter la présentation de Bootstrap
.
Travail à réaliser
- Lisez la documentation sur le thème
Bootstrap 5
pour les formulaires - Ajoutez le thème
Bootstrap 5
pour les formulaires au fichier de configuration deTwig
- Essayez votre modification
- Associez au bouton les classes
CSS
permettant de respecter le styleBoostrap
Validation des données ¶
Le formulaire personnalisé que vous venez de produire comporte un certain nombre de contrôles comme l'obligation de présence des diverses valeurs. Il est possible de renforcer les contrôles pour avoir la certitude que les données fournies par l'utilisateur seront toujours valides.
Travail à réaliser
- Dans «
ContactType
», modifiez les types des champs : - Dans «
ContactType
», modifiez les options de «category
» :- la valeur est optionnelle
- le texte d'invitation au choix est « Catégorie ? »
- Dans «
ContactType
», ajoutez l'option «'empty_data' => ''
» pour toutes les propriétés de type «string
» : «firstname
», «lastname
», «email
» et «phone
»InformationCette dernière option permet de contourner une erreur qui survient lorsqu'un champ de saisie peut admettre un simple espace qui sera nettoyé (voir «
trim
») à la réception des données. Le champ est alors «null
» et cela provoque une erreur dans «handleRequest()
». - Lisez le chapitre « Validating Forms » puis parcourez la liste des possibilités dans « Validation Constraints Reference »
- Importez les contraintes dans l'entité «
Contact
» :use Symfony\Component\Validator\Constraints as Assert;
- Ajoutez sous forme d'attributs
PHP
les contraintes sur les propriétés :- «
NotBlank
», «Length
» max appropriée pour «firstname
» - «
NotBlank
», «Length
» max appropriée pour «lastname
» - «
NotBlank
», «Length
» max appropriée, «Email
» pour «email
» - «
NotBlank
», «Length
» max appropriée, «Regex(pattern: '/^(?:(?:\+|00)33[\s.-]{0,3}(?:\(0\)[\s.-]{0,3})?|0)[1-9](?:(?:[\s.-]?\d{2}){4})$/', message: 'Format de téléphone invalide')
» pour «phone
»
- «
Persistance des modifications d'un « Contact
» existant
¶
Maintenant que vous disposez de l'interface d'édition d'un « Contact
», vous allez modifier votre contrôleur afin qu'il permette de faire persister les modifications soumises par l'utilisateur. Une manière classique d'organiser le code dans le contrôleur consiste à utiliser une seule action pour construire le formulaire et faire persister les données. Le formulaire est affiché lorsque l'action est déclenchée par une requête dont la méthode est GET
et les données sont enregistrées lorsque la méthode est POST
.
Travail à réaliser
- Dans l'action du contrôleur, récupérez la requête en la déclarant comme paramètre de votre action
- Liez les données soumises par l'utilisateur au formulaire
- Vérifiez si le formulaire est posté et s'il est valide
- Dans le cas où le test précédent est valide, enregistrez les données Information
Comme indiqué dans la documentation, il est inutile de demander à faire persister l'objet puisqu'il est issu de la base de données et donc surveillé par
Doctrine
. Il suffit alors d'appeler «flush()
». - Pour finir, Redirigez l'utilisateur vers l'affichage du contact qui vient d'être modifié
- Si le formulaire n'est pas posté ou que les données ne sont pas valides, produisez le rendu du formulaire (le code de cette partie est déjà écrit)
- Essayez ces nouvelles fonctionnalités
- Observez les échanges
HTTP
dans la console de développement de votre navigateur lors de la mise à jour d'un contact : méthodeHTTP
, données transmises au serveur, structure des données transmises, code de réponseHTTP
et redirection
Interface de création d'un « Contact
»
¶
Passez à la création d'un « Contact
». La démarche est proche de celle adoptée pour la modification, à la différence près qu'elle débute avec une nouvelle entité « Contact
» plutôt qu'avec une entité issue de la base de données.
Travail à réaliser
- Dans l'action «
create()
», créez une entité «Contact
» - Construisez un formulaire
ContactType
sur leContact
que vous venez de créer - Transmettez le formulaire à votre « template »
Twig
et affichez-le de la même façon que pour la modification - Ajoutez un bouton d'envoi ayant pour texte « Ajouter », en respectant les bonnes pratiques
- Donnez au bouton les classes
CSS
permettant de respecter le styleBoostrap
- Essayez votre formulaire et constatez qu'il s'affiche
- Essayez d'envoyer votre formulaire sans saisir de nom de contact et constatez le blocage de l'envoi et l'affichage du message d'erreur au niveau du champ «
firstname
» - Ajoutez les contrôles et le code nécessaire à l'enregistrement du nouveau contact
- Vérifiez le bon affichage du nouveau contact
Personnalisation des formulaires en Twig
¶
Les intitulés de champs de saisie sont le nom des propriétés des instances de « Contact
». Une personnalisation et une francisation sont nécessaires. Elles doivent intervenir dans la vue puisqu'elles relèvent de la présentation des données.
Travail à réaliser
- Ouvrez votre fichier de vue «
contact/create.html.twig
» - Remplacez le rendu
Twig
de l'ensemble des champs de formulaire avec «form_widget()
» par une décomposition champs par champ utilisant «form_row()
», ce qui facilitera la personnalisation et la séparation entre la logique du formulaire et sa présentation à l'utilisateur - Ajoutez en
Twig
un «label
» pour chaque champ du formulaire au niveau des «form_row()
» - Déplacez la définition du «
placeholder
» de la catégorie du formulaire «ContactType
» à la vueTwig
- Essayez votre nouvelle formulation de la création d'un contact
Inclusion de template Twig
¶
La personnalisation que vous venez d'apporter au formulaire de création d'un contact devrait être reproduite dans celui d'édition d'un contact. La duplication de code engendrée par la duplication de ces formulaires dans les vues Twig
serait une solution rapide et facile à laquelle vous ne cèderez pas ! Vous allez factoriser le code Twig
du formulaire en le transformant en un sous-modèle que vous inclurez.
Travail à réaliser
- Créez un nouveau « template » «
contact/_form.html.twig
» vide - Déplacez le code
Twig
concernant votre formulaire de «contact/create.html.twig
» vers «contact/_form.html.twig
» - Incluez le « template » du formulaire dans «
contact/create.html.twig
» - Vérifiez le fonctionnement de cette vue remaniée
- Remplacez le texte du bouton d'envoi dans le modèle du formulaire par une variable «
submit_label
» dont la valeur sera fournie lors de l'inclusion du modèle de formulaire dans «contact/create.html.twig
» - Vérifiez que le fonctionnement est toujours correct puisque le modèle inclus a accès aux variables du contexte actif (ceci concerne la variable «
form
» définie dans «contact/create.html.twig
» et utilisée dans «contact/_form.html.twig
») - Ajoutez le paramètre «
with_context = false
» à votre instruction d'inclusion afin d'interdire le partage de variables du contexte local et ainsi minimiser les effets de bord possibles - Constatez que la vue ne fonctionne plus car la variable
Twig
«form
» n'est pas définie - Passez explicitement votre formulaire en paramètre de l'inclusion
- Essayez votre nouvelle formulation de création d'un contact
- Appliquez la démarche d'inclusion du modèle de formulaire à la vue d'édition d'un contact, en pensant à modifier le texte du bouton d'envoi
- Essayez votre nouvelle formulation de l'édition d'un contact
Interface de suppression d'un « Contact
»
¶
Passez à l'interface de suppression d'un Contact
. L'action de suppression pourrait se réaliser sans interface puisqu'il suffit que la ressource « /contact/{id}
» supprime le contact. Vous allez néanmoins construire une vue demandant une confirmation. Cela vous permettra de découvrir le fonctionnement d'un formulaire avec plusieurs boutons d'envoi.
Travail à réaliser
- Lisez le chapitre « How to Submit a Form with Multiple Buttons »
- Dans l'action «
delete()
», construisez à partir du «FormBuilder
» un formulaire comportant deux boutons d'envoi, «delete
» et «cancel
» - Transmettez ce formulaire à la vue et affichez-le en prenant soin de séparer les deux boutons en utilisant une nouvelle fois «
form_start()
», «form_row()
» et «form_end()
» - Modifiez en
Twig
le «label
» des boutons pour afficher « Supprimer » et « Annuler » - Pour distinguer visuellement les deux boutons, modifiez en
Twig
le «attr
» de chacun d'eux pour affecter respectivement la classeCSS
«btn btn-primary
» et «btn btn-secondary
» - Sur le même principe, le formulaire recevra la classe
CSS
«d-flex
» pour en faire un conteneurflex
dans lequel les boutons sont régulièrement espacés - Dans le contrôleur :
- Si le formulaire est soumis et que c'est par le bouton «
delete
», supprimez le contact et redirigez vers la liste des contacts - Si le formulaire est soumis et que ce n'est pas par le bouton «
delete
», redirigez vers la fiche du contact - Si le formulaire n'est pas soumis effectuez le rendu du formulaire
InformationLe fonctionnement attendu présenté ci-dessus donne l'idée générale du traitement à réaliser. À vous de l'interpréter pour en faire un algorithme clair et efficace.
- Si le formulaire est soumis et que c'est par le bouton «
Liens depuis la liste des contacts et derniers réglages ¶
Les nouvelles fonctionnalités d'ajout, d'édition et de suppression devraient être accessibles depuis la liste des contacts ou depuis le menu. Afin d'obtenir une interface avec peu de texte, vous ajouterez des liens matérialisés par des symboles Material
.
Travail à réaliser
- Ajoutez la feuille de style des « Google Fonts » à votre modèle
Twig
de base - Lisez la partie de la documentation décrivant comment utiliser les icônes en
HTML
- Dans la vue des contacts «
templates/contact/_contacts.html.twig
», ajoutez devant le nom de chaque contact : - Complétez votre design en affectant respectivement les classes
CSS
Bootstrap
«text-warning
» et «text-danger
» aux deux précédentes icônes - Ajoutez une entrée de menu « Ajouter un contact » dans la «
Navbar
» - Constatez que ces nouveaux liens font échouer les tests.
- Affectez la classe
CSS
«contact
» au lien autour du « nom, prénom » du contact dans «templates/contact/_contacts.html.twig
» - Mettez à jour les sélecteurs dans les tests
Utilisateurs, authentification et autorisations ¶
La partie précédente a permis de mettre en place la modification des contacts. Il convient à présent de sécuriser ces modifications en introduisant des utilisateurs ainsi que les droits d'accès associés.
Création de l'entité « User
»
¶
Symfony
, via le « MakerBundle
», assiste le développeur dans la création d'une entité dédiée à la gestion des utilisateurs.
Travail à réaliser
- Lisez le chapitre « Create your User Class »
- Effectuez impérativement un «
commit
» si ce n'était pas fait - Créez une entité «
User
» en utilisant les valeurs par défaut :bin/console make:user The name of the security user class (e.g. User) [User]: > Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]: > Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]: > Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server). Does this app need to hash/check user passwords? (yes/no) [yes]: > created: src/Entity/User.php created: src/Repository/UserRepository.php updated: src/Entity/User.php updated: config/packages/security.yaml Success! Next Steps: - Review your new App\Entity\User class. - Use make:entity to add more fields to your User entity and then run make:migration. - Create a way to authenticate! See https://symfony.com/doc/6.3/security.html
- Observez les fichiers créés
- Observez les modifications apportées au fichier «
security.yaml
» avec le contrôle de version dePhpStorm
- Modifiez l'entité «
User
» à l'aide du «MakerBundle
» pour lui ajouter les propriétés :- «
firstname
» de type «string
» de taille «100
» non «nullable
» - «
lastname
» de type «string
» de taille «150
» non «nullable
»
- «
- Créez une migration avec la commande :
bin/console make:migration
- Observez le fichier «
migrations/Version…
» créé par le «MakerBundle
» - Effectuez la migration avec :
bin/console doctrine:migrations:migrate
- Observez les principales modifications apportées à votre base de données dans
phpMyAdmin
Authentification ¶
Les utilisateurs ne peuvent actuellement pas s'authentifier. Vous allez à présent configurer la mécanique et construire les pages de connexion et déconnexion pour les utilisateurs.
Travail à réaliser
- Effectuez impérativement un «
commit
» si ce n'était pas fait - Lisez les grandes étapes du chapitre « Form Login » qui donne les grandes lignes de ce qui sera réalisé par le «
MakerBundle
» - Générez le formulaire d'authentification en appliquant les mêmes valeurs que dans l'exemple suivant :
bin/console make:auth What style of authentication do you want? [Empty authenticator]: [0] Empty authenticator [1] Login form authenticator > 1 The class name of the authenticator to create (e.g. AppCustomAuthenticator): > LoginFormAuthenticator Choose a name for the controller class (e.g. SecurityController) [SecurityController]: > Do you want to generate a '/logout' URL? (yes/no) [yes]: > Do you want to support remember me? (yes/no) [yes]: > no created: src/Security/LoginFormAuthenticator.php updated: config/packages/security.yaml created: src/Controller/SecurityController.php created: templates/security/login.html.twig Success! Next: - Customize your new authenticator. - Finish the redirect "TODO" in the App\Security\LoginFormAuthenticator::onAuthenticationSuccess() method. - Review & adapt the login template: templates/security/login.html.twig.
- Observez les fichiers créés et modifiés tout en faisant le lien avec la documentation que vous venez de survoler Remarque importante
Tous les fichiers générés sont indispensables au bon fonctionnement de l'authentification. Ajoutez-les à votre dépôt !
- Consultez la page de connexion «
/login
» et constatez qu'elle s'affiche correctement - Modifier la vue associée pour traduire les textes en français
- Modifiez la taille du bouton de connexion pour l'étirer sur toute la largeur de son conteneur et appliquez-lui une marge supérieure
- Tentez une connexion qui échouera forcément afin de constater que le message d'erreur est en anglais
- Configurez la langue par défaut à «
fr
» dans «config/packages/translation.yaml
»
« Fixtures » et rôles ¶
Vous possédez à présent une gestion des entités « User
» mais aucun utilisateur n'a été créé. Vous allez le faire par le biais de « fixtures » générées grâce à une forge « Foundry
». Elle va demander un peu d'attention dans la mesure où les mots de passe que vous allez injecter dans l'entité sont fournis en clair alors qu'ils doivent être encodés avant d'être stockés. L'encodage sera automatisé par la définition de la méthode « initialize()
» du « UserFactory
».
Travail à réaliser
- Lancez la création d'une nouvelle forge de données :
bin/console make:factory
- Lisez le chapitre « Factories as Services »
- Recopiez la propriété (que vous typerez), le constructeur ainsi que la méthode «
initialize()
» (dans laquelle vous penserez à adapter la classe manipulée)InformationNous passons très rapidement sur le processus de configuration du hachage des mots de passe dans la forge. Ce point sera revu plus en détails au semestre 4.
- Dans la méthode «
defaults()
», fixez les valeurs du «Contact
» :- le nom soit un nom de famille généré par «
Faker
» - le prénom soit un prénom généré par «
Faker
» - l'email, unique, soit de la forme «
user-123@example.com
», dans lequel «123
» sera généré par la méthode «numerify()
» de «Faker
» - le mot de passe est «
test
»
- le nom soit un nom de famille généré par «
- Créez une nouvelle classe de « fixtures » «
UserFixtures
» :bin/console make:fixtures UserFixtures
- Observez les fichiers créés
- Selon les modalités décrites dans la documentation, modifiez la classe «
UserFixtures
» afin qu'elle ajoute les utilisateurs :- « Tony Stark » dont l'email est « root@example.com » et dont le rôle est «
'ROLE_ADMIN'
» - « Peter Parker » dont l'email est « user@example.com » et dont le rôle est «
'ROLE_USER'
» - 10 utilisateurs aléatoires
- « Tony Stark » dont l'email est « root@example.com » et dont le rôle est «
- Générez une nouvelle version de la base de données :
composer db
- Observez le contenu de la table «
User
» - Mettez à jour la documentation du projet pour fournir les identifiants et mot de passe des utilisateurs de démonstration
Connexion et déconnexion ¶
Tout est maintenant prêt pour vérifier la connexion et la déconnexion des utilisateurs.
Travail à réaliser
- Accédez à la ressource «
/login
» - Tentez de vous connecter avec l'utilisateur «
root@example.com
» dont le mot de passe est «test
»InformationPuisque vous allez souvent vous connecter et vous déconnecter de l'application, mémorisez l'identifiant et le mot de passe dans votre navigateur.
- Constatez le message d'erreur :
TODO: provide a valid redirect inside …/src/Security/LoginFormAuthenticator.php
- Rendez-vous dans le fichier concerné et effectuez une redirection vers la page d'accueil de l'application
- Déconnectez-vous en accédant à la ressource «
/logout
» - Tentez de nouveau de vous connecter avec l'utilisateur «
root@example.com
» dont le mot de passe est «test
» - Vérifiez que la redirection s'est bien produite
- Observez l'information d'identification de l'utilisateur dans la «
WDT
»
Autorisations dans les contrôleurs et les vues ¶
Le modèle authentification / autorisation fonctionne mais manque d'ergonomie. Vous allez modifier certaines vues pour ajouter un bandeau de connexion / déconnexion et restreindre l'affichage aux utilisateurs en fonction de leur rôle.
Travail à réaliser
- En vous inspirant de la fin du chapitre concernant les rôles et à l'aide d'un attribut
PHP
, autorisez l'accès à l'ensemble du contrôleur «CategoryController
» uniquement aux utilisateurs connectés (voir rôle «IS_AUTHENTICATED_FULLY
» ou «IS_AUTHENTICATED_REMEMBERED
») - Déconnectez-vous en accédant à la ressource «
/logout
» - Essayez d'accéder à la liste des catégories et constatez que vous êtes invité à vous connecter
- Modifiez le « template » de base en y ajoutant un couple de boutons pour la connexion ou la déconnexion selon que l'utilisateur est connecté ou non Information
Bootstrap
permet de donner un « style bouton » à des liens. - Associez respectivement les routes de connexion et de déconnexion aux deux précédents boutons
- Essayez les boutons et vérifiez le nouvel affichage lorsqu'un utilisateur est connecté ou non connecté
- Lisez la documentation concernant l'utilisateur dans le chapitre « The App Global Variable »
- Modifiez le texte du bouton de déconnexion pour y ajouter le prénom de l'utilisateur
- Vérifiez si un utilisateur est administrateur pour afficher les liens d'édition et de suppression de contact dans la vue «
templates/contact/_contacts.html.twig
» - Vérifiez si un utilisateur est connecté pour afficher le lien de création d'un contact dans la «
NavBar
»Remarque importanteMasquer les liens vers les ressources ne limite en rien l'accès aux ressources. Il faut également limiter l'accès aux actions !
- Limitez l'accès aux actions concernées
Mise à jour des tests ¶
La restriction d'accès à la création, la modification et la suppression d'un contact fait échouer les tests. Il convient de les mettre à jour.
Travail à réaliser
- Dans le test «
form()
» de «UpdateCest
» :- Créez un utilisateur ayant le rôle administrateur et récupérez l'objet réel avec la méthode «
_real()
» - Connectez l'utilisateur créé
- Vérifiez que le test passe
- Profiter de l'occasion pour renommer la méthode de test «
form()
» en «formShowsContactDataBeforeUpdating()
»
- Créez un utilisateur ayant le rôle administrateur et récupérez l'objet réel avec la méthode «
- Ajoutez un test «
accessIsRestrictedToAuthenticatedUsers()
» dont l'objectif sera de vérifier que l'accès est bien restreint aux utilisateurs connectés :- Créez un contact
- Accédez à la ressource permettant de le modifier
- Faites l'assertion que vous êtes sur la route de connexion
- Vérifiez que le test passe
- Ajoutez un test «
accessIsRestrictedToAdminUsers()
» dont l'objectif sera de vérifier que l'accès est bien restreint aux administrateurs :- Créez un contact
- Créez un utilisateur ayant le role utilisateur
- Connectez l'utilisateur créé
- Accédez à la ressource permettant de le modifier
- Faites l'assertion que la réponse
HTTP
est «FORBIDDEN
» - Vérifiez que le test passe
- Effectuez les mêmes modifications pour «
DeleteCest
» - Ajustez le test de création de contact et complétez avec un test de contrôle d'accès
- Vérifiez que l'ensemble des tests passe Information
Il faudrait ajouter des tests pour valider le fonctionnement des divers formulaires mais nous n'en avons pas le temps…
Gestion de l'application : « back-office » avec EasyAdmin
¶
Dans un site ou une application Web, le « back-office », littéralement l'arrière-boutique, représente toute la partie non visible par les utilisateurs dédiée à la gestion par les administrateurs. Vous pouvez développer entièrement ce « back-office » mais, puisqu'il consiste principalement en une multitude de « CRUD
», c'est une tâche très ingrate qui est en partie automatisable. EasyAdmin
est un « bundle » Symfony
spécialement conçu pour construire la partie administration d'une application Symfony
.
Installation de EasyAdmin
¶
EasyAdmin
est évidemment accessible par Composer
. Vous allez donc en faire une dépendance du projet puis générer le tableau de bord.
Travail à réaliser
- Installez EasyAdmin dans votre projet :
composer require admin
- Observez les modifications apportées au projet
- Créez le tableau de bord (« dashboard ») :
php bin/console make:admin:dashboard
- Observez les modifications apportées au projet
- Accédez à la ressource «
/admin
»
Page d'accueil du « back-office » ¶
Vous allez proposer un ensemble de fonctionnalités de gestion aux administrateurs de votre application. Elles seront regroupées dans la page d'accueil du « back-office ».
Travail à réaliser
- Lisez le code d'exemple en commentaire dans l'action «
index()
» du «DashboardController
» - Dans le tutoriel « SymfonyCasts » dédié à
EasyAdmin
, lisez la note concernant la page d'accueil depuis la version 4.0.3 - Supprimez les commentaires
- Remplacez l'instruction :
return parent::index();
par :return $this->render('admin/index.html.twig');
- Générez le fichier
Twig
à l'aide dePhpStorm
- Insérez le code suivant dans le « template » :
{% extends '@EasyAdmin/page/content.html.twig' %}
- Vérifiez l'apparition du squelette du « back-office » lorsque vous accédez à la ressource «
/admin
»
« CRUD » pour les entités ¶
Le squelette du back-office est fonctionnel mais vide. Vous devez donc y ajouter le « CRUD
» de chacun de vos entités. Ceci va consister en la génération, par le « MakerBundle
» d'un contrôleur dédié par entité.
Travail à réaliser
- Lisez l'introduction de « CRUD Controllers »
- Lancez la création d'un contrôleur pour l'entité «
Category
» :bin/console make:admin:crud
- Observez le fichier produit
- Sur le modèle du commentaire présent dans la méthode «
configureMenuItems()
» du «DashboardController
», ajoutez un lien vers le «CRUD
» de «Category
»InformationLe mot-clé «
yield
» permet de construire un générateur. Chaque appel à «yield
» se comporte comme un «return
» qui n'arrêterait pas l'exécution de la fonction (ou méthode) génératrice.Pour compléter l'exemple de la documentation officielle de
PHP
, le programme suivant : produit comme résultat :generate 1 1 generate 2 2 generate 3 3
Si vous n'êtes pas à l'aise avec ce principe, vous pouvez retourner un tableau, de la même façon que dans la documentation.
- Constatez l'apparition de nouvelles fonctionnalités en consultant la ressource «
/admin
» - Lancez la création d'un contrôleur pour les entités «
Contact
» et «User
» : - Ajoutez deux nouvelles entrées au menu du « back-office »
- Constatez l'apparition des nouvelles fonctionnalités
Autorisations dans le « back-office » ¶
Si vous n'êtes actuellement pas connecté, vous pouvez accéder au « back-office » et agir sur l'ensemble de la base de données. Cette situation n'est pas normale et doit être corrigée.
Travail à réaliser
- Lisez le chapitre « Restrict Access to the Entire Backend »
- Limitez l'accès au « back-office » en modifiant la configuration générale de sécurité de
Symfony
dans le fichier «config/packages/security.yaml
» - Vérifiez que la restriction d'accès est effective
Configuration du « CRUD
» de « Contact
»
¶
L'interface générée par défaut par EasyAdmin
ne reflète pas les relations. Vous devez les configurer manuellement.
Travail à réaliser
- En partant de «
/admin
», accédez à la liste des contacts - Constatez l'absence de la catégorie de chaque contact
- Éditez à présent un contact
- Constatez l'absence de la catégorie du contact
- Lisez le paragraphe « Configuring the Fields to Display »
- Configurez les champs pour l'entité «
Contact
» en trouvant le type le plus approprié dans la documentation - Si vous avez configuré le champ «
category
», vous devriez constater l'erreur suivante :Object of class Proxies\__CG__\App\Entity\Category could not be converted to string - Puisque vous avez déjà effectué un travail similaire dans un formulaire, utilisez la même option «
choice_label
» que vous appliquerez à l'aide de la méthode «setFormTypeOption()
» - Vérifiez que la liste de choix des catégories est présente dans le formulaire d'édition du contact
- Constatez que la liste de choix des catégories n'est pas triée
- Réutilisez encore une fois vos reconnaissances passées pour appliquer un tri sur la source de données
- Vérifiez que la liste de choix des catégories est correcte
- Constatez que l'identifiant du contact est présent dans le formulaire et peut donc être modifié
- Lisez le début du paragraphe « Displaying Different Fields per Page »
- Masquez l'identifiant dans le formulaire
- Revenez sur la liste des contacts
- Constatez que la catégorie de chaque contact est affichée sous la forme «
Category #id
» - Lisez le paragraphe « Formatting Options »
- Utilisez «
formatValue()
» pour retenir le nom de la catégorie comme valeur affichée dans le contactRemarque importantePuisque la catégorie peut être nulle, il est important d'effectuer un accès sûr (« nullsafe ») à son nom.
Configuration du « CRUD
» de « User
»
¶
La gestion des utilisateurs dans le « back-office » est essentielle mais demande quelques ajustements pour gérer correctement le mot de passe. En effet, celui-ci est stocké sous une forme hachée et doit donc être traité lors de sa modification. Cependant, il faut veiller à ne pas hacher plusieurs fois le mot de passe lors de l'édition des autres champs.
Remarque importante
Proposer aux administrateurs de modifier le mot de passe d'un utilisateur n'est ni courant ni conforme aux bonnes pratiques de sécurité et de confidentialité. Cependant, dans ce sujet de TP, cela représente une occasion de découvrir comment effectuer des traitements sur les données des formulaires d'édition ou de création d'entité de EasyAdmin
avant de mettre à jour la base de données.
Le fonctionnement va donc consister en la configuration du champ du mot de passe pour qu'il soit vide par défaut. S'il est reçu vide, aucune modification ne doit être faite. S'il est reçu non vide, il doit être haché avant d'être affecté à la propriété correspondante de l'entité en cours d'édition. Pour cela, le contrôleur devra utiliser le contexte qui permettra d'accéder à la requête HTTP
et donc d'accéder aux valeurs saisies. Il devra également utiliser un « UserPasswordHasherInterface
» pour hacher le mot de passe qui est saisi en clair. Le service de hachage de mot de passe sera injecté dans le constructeur du contrôleur.
Le contrôle de présence et le hachage du mot de passe devront être effectués à la mise à jour de l'utilisateur mais également lors de l'insertion d'un nouvel utilisateur. Ceci correspond respectivement aux méthodes « updateEntity()
» et « persistEntity()
» du « UserCrudController
».
Travail à réaliser
- Consultez la liste des utilisateurs
- Affichez la page d'édition d'un utilisateur
- Constatez que le mot de passe haché apparaît dans le formulaire
- Configurez les champs pour l'entité «
User
»InformationLes rôles de l'utilisateur sont stockés dans un tableau
PHP
et sous forme d'une chaîne de caractèresJSON
dans la base de données. La correspondance est gérée parDoctrine 2
. Le type de champEasyAdmin
correspondant est «ArrayField
» - Masquez l'identifiant dans le formulaire
- Donnez les caractéristiques suivantes au champ mot de passe :
- affiché uniquement dans les formulaires
- type de champ de formulaire «
PasswordType
» - non obligatoire
- non associé à l'entité
- valeur vide contenant une chaîne vide
- autocomplétion désactivée pour la saisie d'un nouveau mot de passe
- Ajoutez une propriété privée «
UserPasswordHasherInterface
» au «UserCrudController
» - Injectez cette propriété à la construction du «
UserCrudController
» - Parcourez « Customizing CRUD Actions », et lisez en particulier le point « Creating, Persisting and Deleting Entities »
- À l'aide de l'autocomplétion de
PhpStorm
, surchargez la méthode «updateEntity()
» (qui correspond à la mise à jour d'une entité) dans laquelle vous préserverez l'appel à la méthode parenteInformationDans la classe et en dehors de toute méthode, vous pouvez obtenir la liste des méthodes « surchargeables » en utilisant le raccourci clavier «
CTRL+SPACE
». Vous pouvez également saisir une partie du nom de la méthode à surcharger pour voir apparaître la liste des suggestions. - Dans la méthode «
updateEntity()
», récupérez le mot de passe saisi par l'utilisateur en utilisant la requêteHTTP
accessible depuis le contexte (méthode «getContext()
»)InformationInspectez le champ du mot de passe dans votre navigateur pour savoir ce que vous cherchez.
Un champ de saisie
HTML
dont le nom est de la forme «name[key]
» se trouvera du côté serveur, une fois analysé par le moteurPHP
, comme un champ nommé «name
» de type tableau et contenant une clé «key
» permettant d'accéder à la valeur soumise par l'utilisateur. - Si le mot de passe n'est pas vide, donnez la version hachée du mot de passe comme valeur de la propriété «
password
» de l'instance de l'entité, d'une manière similaire à ce qui a été réalisé avec «Foundry
» - Vérifiez que vous pouvez modifier correctement le mot de passe d'un utilisateur Information
Vous devez modifier le mot de passe d'un utilisateur et essayer de vous connecter avec le nouveau mot de passe pour vérifier que cela fonctionne. Pour faciliter la procédure, utilisez deux navigateurs : un dans lequel vous êtes connecté comme administrateur pour modifier le mot de passe et un autre dans lequel vous effectuez les tentatives de connexion.
- Surchargez la méthode «
persistEntity()
» (qui correspond à l'ajout d'une entité) de la même manière que pour la méthode «updateEntity()
»InformationVous vous apprêtez à reproduire le code de vérification et de hachage du mot de passe. Vous avez donc le choix entre copier le code et le factoriser. Vous allez évidemment le factoriser !
- Sélectionnez les quelques lignes de code dédiées au contrôle de présence du mot de passe et à son hachage dans la méthode «
updateEntity()
» - Dans le menu « Refactor » de
PhpStorm
, sélectionnez « Refactor This… » - Dans le menu contextuel choisissez « Extract Method… » et saisissez le texte «
setUserPassword
» comme nom de méthode - Supprimez les annotations inutiles et donnez un type et un nom explicites au paramètre de la méthode
- Utilisez la méthode «
setUserPassword
» dans «persistEntity()
» - Vérifiez que l'ajout d'un utilisateur se déroule correctement
Un peu de style ¶
La liste des utilisateurs peut être améliorée et c'est l'occasion de découvrir comment ajouter du style dans le « back-office ».
Travail à réaliser
- Revenez sur la liste des utilisateurs
- Vérifiez que les rôles des utilisateurs s'affichent bien sous la forme «
ROLE_ADMIN, ROLE_USER
» ou «ROLE_USER
»InformationSi vous consultez la base de données, vous constaterez que les utilisateurs ayant un tableau de rôles vide en base de données («
[]
») s'affichent ici avec un rôle «ROLE_USER
». Ceci est dû au fait que le rôle minimal est «ROLE_USER
». Vous pouvez le constater en consultant le code de la méthode «getRoles()
» de la classe «User
». - Pour commencer à remplacer cet affichage texte par des symboles
Material
, vous allez ajouter la feuille de style correspondante dans le «DashboardController
» - Utilisez la méthode «
formatValue()
» sur le champ associé aux rôles de l'utilisateur pour :- Retourner l'icône «
manage_accounts
» si le rôle «ROLE_ADMIN
» est présent - Retourner l'icône «
person
» si le rôle «ROLE_USER
» est présent - Retourner une chaîne vide sinon
- Retourner l'icône «
Et après ? ¶
Votre application de gestion de contacts est fonctionnelle mais peut être étoffée. Ce que vous avez réalisé est normalement suffisant pour mener à bien votre SAÉ.
Vous pouvez poursuivre ce TP en proposant la création, l'édition et la suppression de catégories, dans l'interface de l'application et dans le « back-office ». Vous pouvez compléter les tests de création, modification et suppression de contact. Vous pouvez proposer une page de profil utilisateur modifiable et toute autre fonctionnalité qui vous semble intéressante.