Jérôme Cutrona

Version statique de l'intranet de Jérôme Cutrona - Rejoindre la version dynamique 🔒

Certains exemples et corrections sont susceptibles de ne pas être opérationnels.

B.U.T. Informatique - IUT de Reims - Université de Reims

Symfony 7.3

Navigation

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 7
  • 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 mécaniquement 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 le 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 code
    php -r "code PHP"
  • à l'aide de la commande php pour exécuter un programme
    php -f un_script_PHP
    ou
    php 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 dans Symfony, s'utilise comme un script shell, et est écrit en PHP. La première ligne de ce script 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 un 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 enrichies de plusieurs dizaines de mégaoctets de cache générés par Symfony.

Vous allez créer l'application basée sur Symfony à l'aide de l'outil « symfony » en ligne de commande (« Symfony CLI ») et pour cela, vous devrez 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. Il est néanmoins utile 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 du projet et ne peut pas fonctionner normalement lorsque ces fichiers sont sur un partage NFS. Afin d'améliorer les performances de l'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, il est impératif de synchroniser le dépôt distant (en ligne de commande ou avec PhpStorm)

git commit
puis
git push
faute 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 de saturer le 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 le dépôt local
    cd /working/votre_login/symfony-contacts
    git pull
  • Effacer le 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 le 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 du projet, vous devez (ré)installer les composants nécessaires à son fonctionnement :

composer install

Vous devrez également reconfigurer l'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
  1. 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
    --2025-09-08 14:06:03--  https://get.symfony.com/cli/installer
    Résolution de get.symfony.com (get.symfony.com)… 18.164.52.20, 18.164.52.7, 18.164.52.11, ...
    Connexion à get.symfony.com (get.symfony.com)|18.164.52.20|:443… connecté.
    requête HTTP transmise, en attente de la réponse… 200 OK
    Taille : 6100 (6,0K) [binary/octet-stream]
    Enregistre : ‘STDOUT’
    
    -                                 100%[=============================================================>]   5,96K  --.-KB/s    ds 0s      
    
    2025-09-08 14:06:03 (2,49 GB/s) — envoi vers sortie standard [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 6151k  100 6151k    0     0  7388k      0 --:--:-- --:--:-- --:--:-- 41.6M
      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'
    
  2. Modifiez ou créez le fichier « .profile » (à la racine de votre compte) afin d'y ajouter
    export PATH="$HOME/.symfony5/bin:$PATH"
  3. Chargez les modifications du « .profile »
    source ~/.profile
    Information

    La 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 modification du fichier « .profile », vous devez vous déconnecter de la machine et vous reconnecter.

  4. Vérifiez le bon fonctionnement de l'exécutable « symfony »
    symfony self:version
  5. 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.3/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.42 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, pgsql, 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.
    
    Information

    Dans 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
  1. Si ce n'est pas déjà fait, installez Composer
    Information

    L'outil « symfony » en ligne de commande (« Symfony CLI ») utilise Composer 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.

  2. Vérifiez que Composer fonctionne correctement :
    composer about
  3. Mettez à jour Composer :
    composer self-update
  4. 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)
  5. Lancez la création d'un nouveau projet Symfony version « 7.3.* » :
    symfony --version 7.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, l'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
  1. Placez-vous le répertoire de l'application
  2. Dans le terminal, lancez le serveur Web local avec la commande suivante :
    symfony serve
    Information

    Le serveur Web fonctionnera tant que vous n'aurez pas terminé l'exécution l'outil ligne de commande « symfony » avec « CTRL+C ».

  3. Suivez les instructions de la partie « Gestion du certificat du serveur Web de l'API dans le navigateur » (uniquement cette partie !)
  4. Accédez à l'URL « https://127.0.0.1:8000 » pour obtenir : Test de l'environnement de développement
  5. Constatez l'apparition de la Web Debug Toolbar
  6. Observez les sorties texte du serveur Web local
  7. Naviguez rapidement à travers les outils de la Web Debug Toolbar
  8. Observez les sorties texte du serveur Web local
  9. 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 l'application Symfony.

Dépôt local

Vous allez configurer le dépôt Git local.

Travail à réaliser
  1. Assurez-vous d'être dans le répertoire du projet symfony-contacts
  2. Constatez que le dépôt Git local a été initialisé par l'outil « symfony » en ligne de commande (« Symfony CLI »)
    git log
  3. Excluez du suivi de Git le répertoire « .idea » de PhpStorm
    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 :

  1. Initialisez le dépôt Git
    git init
  2. Ajoutez l'ensemble des fichiers à l'index
    git add .
  3. Effectuez la première validation
    git commit -m "Initial commit"
  4. 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
  1. Créez un nouveau projet « symfony-contacts » sur GitLab (pensez à décocher la case « Initialize repository with a README »)
  2. Associez le dépôt local et le dépôt distant
  3. Poussez la 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 : Configuration de Git

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. Configuration de Git

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 l'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 JetBrains 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
  1. Dans le terminal et dans le répertoire du projet, lancez la commande
    phpstorm . &
    Information

    Cette 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 de PhpStorm ou dans son menu « File » puis « Recent Projects ».

  2. Patientez pendant l'indexation des fichiers du projet visible dans la partie droite de la barre d'état Indexation du projet
  3. Activez le greffon « Symfony Plugin » si ce n'était pas déjà fait et que cela vous est proposé Proposition d'auto-configuration du greffon Symfony
  4. Vérifiez que le greffon « Symfony Plugin » est bien installé dans PhpStorm (menu « Settings… » puis « Plugins »)

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 l'application. Comme l'année dernière, vous utiliserez PHP CS Fixer pour vérifier et corriger le style de le code PHP. Vous utiliserez un outil équivalent pour le code Twig.

Travail à réaliser
  1. Installez PHP CS Fixer en vous référant au tutoriel Installation et configuration de PhpStorm pour configurer PHP CS Fixer
  2. Rendez-vous dans les préférences de PhpStorm dans « PHP → Quality Tools » et vérifiez que PHP CS Fixer est bien configuré Configuration de PHP CS Fixer dans PhpStorm
  3. 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
    Nothing to modify in lock file
    Writing lock file
    Installing dependencies from lock file (including require-dev)
    Nothing to install, update or remove
    Generating autoload files
    123 packages you are using are looking for funding.
    Use the `composer fund` command to find out more!
    
    Symfony operations: 1 recipe (dffb4f3dfb06b7c1bbc163a1f4fd34f6)
      -  WARNING  vincentlanglet/twig-cs-fixer (>=3.0): 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/3.0
    
        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 (>=3.0): From github.com/symfony/recipes-contrib:main
    Executing script cache:clear [OK]
    Executing script assets:install public [OK]
    Executing script importmap:install [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.
    
     vincentlanglet/twig-cs-fixer  instructions:
    
    
     You can create a `.twig-cs-fixer.dist.php` and configure to your needs 
     See https://github.com/VincentLanglet/Twig-CS-Fixer/blob/main/docs/configuration.md 
     For configuration explanation. 
    
    No security vulnerability advisories found.
    Using version ^3.9 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 du projet dans un fichier « REAMDE.md » et le tenir à jour au fil du développement.

Travail à réaliser
  1. Créez le fichier « README.md » à l'aide de PhpStorm (qui vous propose de l'ajouter à l'index Git)
  2. Complétez les informations suivantes :
    1. un titre de niveau 1 contenant le titre explicite du projet
    2. 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)
    3. un titre de niveau 2 « Installation / Configuration » précisant les points essentiels permettant d'installer, de configurer et de lancer le projet
  3. Validez votre travail dans Git

Mise en place de scripts Composer

Les dépendances du 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
  1. Ajoutez un script « start » qui lance le serveur web de test (« symfony serve ») sans restriction de durée d'exécution
  2. Ajoutez un script « test:phpcs » qui lance la commande de vérification du code par PHP CS Fixer
  3. Ajoutez un script « fix:phpcs » qui lance la commande de correction du code par PHP CS Fixer
  4. Ajoutez un script « test:twigcs » qui lance la commande de vérification du code par Twig CS Fixer
  5. Ajoutez un script « fix:twigcs » qui lance la commande de correction du code par Twig CS Fixer
  6. Ajoutez un script « test » qui lance les scripts Composer « test:phpcs » et « test:twigcs »
  7. Ajoutez un script « fix » qui lance les scripts Composer « fix:phpcs » et « fix:twigcs »
  8. Documentez vos scripts dans le fichier « composer.json »
  9. Documentez vos scripts dans le fichier « README.md »
  10. Validez votre travail dans Git
  11. Relancez le 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 interactions client/serveur et perturbe votre compréhension du fonctionnement de Symfony. Vous allez donc le désactiver.

Travail à réaliser
  1. Ouvrez le fichier « assets/controllers.json »
  2. 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 l'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 : Symfony request flow

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
  1. Observez les routes disponibles dans l'environnement de production :
    bin/console debug:router --env=prod
  2. 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
  3. Observez les routes disponibles dans l'environnement de développement, qui est celui par défaut dans la 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 » Web Debug Toolbar
    • celles commençant par « _profiler » qui sont destinée au « Profiler » (profilage de l'application lors de son exécution)
  4. Observez la configuration du routage « Profiler » en ouvrant le fichier « config/routes/web_profiler.yaml »
  5. 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
  1. Complétez vos connaissances en lisant au moins la partie « Creating Routes as Attributes » de la documentation.
  2. Créez un nouveau contrôleur « HelloController » à l'aide du « MakerBundle », sans générer les tests PHPUnit proposés par le « MakerBundle » :
    bin/console make:controller Hello
    
     Do you want to generate PHPUnit tests? [Experimental] (yes/no) [no]:
     > 
    
     created: src/Controller/HelloController.php
     created: templates/hello/index.html.twig
    
               
      Success! 
               
    
     Next: Open your new controller class and add some pages!
    
    Information

    Le « 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é.

  3. Ouvrez le fichier du contrôleur dans PhpStorm
  4. Observez l'ensemble des routes disponibles pour l'application avec la commande
    bin/console debug:router
  5. Observez l'ensemble des routes disponibles pour l'application ainsi que les contrôleurs associés avec la commande
    bin/console debug:router --show-controllers
  6. Faites le lien entre la première route disponible et le code généré par le « MakerBundle »
  7. Observez les détails de la route nommée « app_hello » grâce à la commande
    bin/console debug:router app_hello
  8. Ajoutez l'action et la route associée qui suivent dans le contrôleur :
        #[Route('/hello/world')]
        public function world(): Response
        {
            return new Response('Hello world!');
        }
    
  9. 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 commande
    bin/console debug:router
  10. Essayez l'URL « http://localhost:8000/hello/world »
  11. Regardez le code source HTML du contenu reçu par le navigateur
  12. Essayez l'URL « http://localhost:8000/hello/guys »
  13. Constatez la réponse reçue par le 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
  1. Complétez vos connaissances en lisant au moins la partie « Route Parameters » de la documentation.
  2. Remplacez le texte « world » du chemin « /hello/world » de la route par un paramètre « name »
  3. Adaptez le prototype de l'action « world() » afin qu'elle puisse recevoir la valeur du paramètre « name » de la route
  4. Observez la nouvelle formulation de la route de l'application avec la commande
    bin/console debug:router
  5. Remplacez le mot « world » de la chaîne « Hello world! » par la valeur du paramètre de l'action
    Information

    Ce 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.

  6. Essayez l'URL « http://localhost:8000/hello/world »
  7. Essayez l'URL « http://localhost:8000/hello/bob »
  8. Constatez la réponse reçue par le navigateur et son code HTTP
  9. 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
  1. Saisissez l'URL « http://localhost:8000/hello/world » dans le navigateur
  2. Dans l'action « world() » du « HelloController », demandez le rendu d'un modèle Twig en remplaçant l'instruction
    return new Response('…
    par
    return $this->render('hello/world.html.twig');
  3. Essayez l'URL « http://localhost:8000/hello/world »
  4. Constatez l'erreur signalée par Symfony ainsi que le code réponse HTTP reçu par le navigateur
  5. Créez le fichier « world.html.twig » manuellement en effectuant un clic droit sur le répertoire « templates/hello » dans PhpStorm et en utilisant le menu « New » puis « File »
  6. Essayez l'URL « http://localhost:8000/hello/world »
  7. Constatez la réponse vide reçue par le navigateur
  8. Ajoutez le texte « Hello world! » dans le fichier « templates/hello/world.html.twig »
  9. Essayez l'URL http://localhost:8000/hello/world
  10. Constatez que la « Web Debug Toolbar » n'est pas présente
  11. Regardez le code source HTML du contenu reçu par le navigateur
  12. Faites en sorte que le fichier « templates/hello/world.html.twig » propose une structure HTML correcte dont le titre sera « Hello World! » et qui contiendra le paragraphe « Hello World! »
  13. Essayez l'URL « http://localhost:8000/hello/world »
  14. Notez l'apparition de la « Web Debug Toolbar ».
  15. Cliquez sur un des éléments de la « Web Debug Toolbar » pour accéder au profileur
  16. 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
  1. Saisissez l'URL « http://localhost:8000/hello/bob » dans le navigateur
  2. Lisez et observez les exemples de « Rendering Templates » et « Creating Templates »
  3. 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 que Symfony 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èle Twig. Afin de maintenir une logique globale et faciliter la compréhension du code, il est vivement conseillé de préserver le même nom pour l'utilisation dans le modèle Twig.

  4. Utilisez la valeur dans la vue pour produire « Hello la_valeur_du_paramètre! » dans le titre du document HTML et dans le paragraphe
  5. Vérifiez le bon fonctionnement de cette nouvelle version de la vue dans le navigateur Web
  6. Saisissez l'URL « http://localhost:8000/hello/bob<script> » dans le navigateur (oui, il y a bien « <script> » à la fin de l'URL !)
  7. Observez le résultat dans le navigateur
  8. Observez le code source HTML du résultat dans le navigateur

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
  1. Modifiez la vue « world.html.twig » afin qu'elle hérite de « base.html.twig »
  2. Remplacez la structure HTML du modèle par l'utilisation des blocs définis dans « base.html.twig »
    Information

    Profitez des suggestions de PhpStorm pour retrouver simplement les noms des blocs disponibles hérités du modèle « base.html.twig ».

  3. Accédez à « http://localhost:8000/hello/bob »
  4. Vérifiez le code source du résultat dans le navigateur
  5. Constatez tous les éléments CSS et JavaScript ajoutés au code source final par l'héritage

Prise en main rapide de Twig

Vous allez explorer quelques-unes des possibilités offertes par Twig.

Travail à réaliser
  1. Utilisez une structure algorithmique Twig « for » pour afficher 10 fois le paragraphe de salutation dans le nouveau « template »
  2. 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) »)
  3. Utilisez la fonction « cycle » de Twig pour afficher alternativement en rouge puis vert (un attribut HTML « style="" » suffira pour cet essai.
  4. Ajoutez un filtre sur le nom afin qu'il apparaisse avec la première lettre en majuscule
  5. Installez le paquet Composer « twig/intl-extra » pour bénéficier des fonctions d'internationalisation de Twig
  6. 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 le 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
  1. Consultez les règles de bonnes pratiques concernant les contrôleurs
  2. Créer une action vide « manyTimes() » dans le contrôleur « HelloController »
    Information

    En saisissant « pubf » suivi de « TAB » dans une classe, PhpStorm génère automatiquement

        public 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.

  3. Associez à cette action une route dont le chemin est « /hello/name/times » paramétré par « name » et « times », en ajoutant l'attribut « #[Route(… »
  4. Si vous décidez de nommer la route, utilisez le nommage standard « app_nomducontroleur_nomdelaction » (c'est un exemple !)
  5. Modifiez l'action pour que ses paramètres soient en accord avec ceux du chemin de la route
  6. Consultez les règles de bonnes pratiques concernant le nommage des modèles Twig
  7. Retournez le résultat la méthode « render() » du modèle « hello/many_times.html.twig »
  8. Utilisez les suggestions de PhpStorm (« Alt+Entrée ») pour générer automatiquement ce nouveau modèle : Twig: Create Template
    Remarque importante

    Si la génération automatique du « template » n'est pas proposée par PhpStorm, c'est que vous n'avez pas installé le plugin Symfony : Symfony plugin installed que vous ne l'avez pas activé lorsque cela vous a été proposé : Symfony plugin enabled ou qu'il n'est pas actif pour ce projet : Symfony plugin enabled for this project

  9. Modifiez le code Twig en prenant « world.html.twig » comme base :
    • Remplacez le titre de la page Web par « Hello many times! »
    • Remplacez le paragraphe de salutation par « Hello many times valeur_du_paramètre_name! »
    • Remplacez l'alternance des couleurs par une alternance entre bleu et orange
    • Affichez « times » fois le paragraphe de salutation
  10. 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
  1. Complétez la route pour que son second paramètre times soit un chiffre en utilisant la validation de paramètres
  2. 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 !)
  3. 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
  4. 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
  5. Rendez le paramètre « times » optionnel qui vaudra par défaut « 3 ».
  6. Utilisez la « Web Debug Toolbar » pour visualiser quelle est la route déclenchée par l'URL « http://localhost:8000/hello/bob »
  7. 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 par Symfony)

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
  1. Observez l'incohérence entre l'« URL » « /hello/bob/42 » demandée et les 3 messages affichés dans le navigateur
  2. Lisez le chapitre concernant les redirections
  3. 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édemment
    Information

    Si vous tapez les guillemets du premier paramètre de la méthode « redirectToRoute() », PhpStorm doit vous proposer la liste de routes disponibles. Suggestion de routes dans redirectToRoute(

  4. Vérifiez le fonctionnement en observant la redirection de « /hello/bob/42 » dans la barre de développement de le 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
  1. 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
  2. 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
  3. Consultez les règles de production de liens vers des ressources de l'application
  4. Dans une liste à puces, créez un lien relatif qui permet de produire le résultat de « 5 » « Hello » à « Joe »
    Information

    PhpStorm peut vous proposer les routes existantes dans les fonctions Twig « path() » ou « url() », profitez-en.

  5. Dans la liste à puces, ajoutez un lien absolu qui permet de produire le résultat de « 8 » « Hello » à « Bob »
  6. Vérifiez le bon fonctionnement de vos liens
  7. Vérifiez que vos deux liens sont bien respectivement relatif et absolu en observant le code HTML reçu par le 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
  1. Installez Codeception et ses modules « asserts » et « symfony » à l'aide de Composer :
    composer require --dev --no-interaction codeception/codeception codeception/module-asserts codeception/module-symfony
    Information

    L'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 recettes Flex de Codeception. 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é.

  2. Initialisez Codeception pour le projet :
    php vendor/bin/codecept bootstrap --namespace=App\\Tests --empty
  3. Chargez les paramètres de configuration de Codeception selon l'environnement de Symfony en complétant le fichier « codeception.yml » :
    params:
        - .env
        - .env.test
    
    

Tests de l'action « manyTimes() » du contrôleur « HelloController »

Travail à réaliser
  1. Créez une nouvelle suite de tests que nous dédirons aux contrôleurs :
    php vendor/bin/codecept generate:suite Controller
  2. Mettez au propre le source PHP généré :
    composer fix:phpcs
    Information

    Les 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 ».

  3. Créez un « Cest » (« Codecept » + « Test ») dédié à l'action « manyTimes() » du « HelloController » :
    php vendor/bin/codecept generate:cest Controller Hello\\ManyTimes
    Information

    Si vous êtes sur Windows et que vous utilisez le terminal cmd ou PowerShell, le caractère « \ » n'a pas le même sens qu'en environnement Linux ou MacOS. Il n'est donc nécessaire de le doubler dans la ligne de commande.

  4. Mettez au propre le source PHP généré :
    composer fix:phpcs
  5. 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:
    
  6. Lancez la construction des classes « Actor » de Codeception pour prendre en compte les nouveaux modules :
    php vendor/bin/codecept build
  7. Testez le style du code PHP :
    composer test:phpcs
    Information

    Les fichiers PHP dont le style n'est pas conforme sont générés par Codeception et exclus du dépôt Git. Il est donc nécessaire de modifier la configuration de PHP CS Fixer pour qu'il n'essaie pas de les corriger.

  8. Modifiez la configuration de PHP CS Fixer dans le fichier « .php-cs-fixer.dist.php » pour exclure le répertoire « tests/Support/_generated »
  9. Testez de nouveau le style du code PHP pour vérifier que le répertoire est bien exclu de l'analyse :
  10. Remplacez le fichier des tests du contrôleur par l'ensemble de tests « ManyTimesCest.php » (télécharger)
    Information

    Ces tests fournis sont assez détaillés pour que vous les utilisiez comme base d'exemple.

  11. Lancez les tests à l'aide de la commande
    php vendor/bin/codecept run
  12. Vérifiez que le 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
  • Complétez le script « test » pour qu'il lance le précédent script à la suite de ceux déjà présents
  • 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 créer le projet d'application Symfony. Des valeurs par défaut ont été affectés à divers paramètres de configuration dans le fichier « .env » situé à la racine du projet.

Remarque importante

Le fichier « .env » sera inclus dans le 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 l'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
  1. Dans le fichier « .env », observez les exemples de configuration de la base de données à travers les valeurs de la variable « DATABASE_URL »
    Information

    Les 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.

  2. Afin de faciliter le travail de correction de votre production, modifiez la ligne en commentaire de la variable « DATABASE_URL » dans le fichier « .env » pour qu'elle corresponde à une base de données MariaDB 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"
    
    devient
    DATABASE_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"
  3. Copiez le fichier « .env » en « .env.local » à partir de PhpStorm et ouvrez-le
  4. Donnez les valeurs adéquates à la variable DATABASE_URL afin de vous connecter sur le serveur dont le nom DNS 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ées MySQL (MariaDB 10.2.25) nommée « cutron01_contact_demo »
    Information

    Retrouvez les parties constitutives d'une URL dans le sujet de TP sur le proptocole HTTP 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… »).

  5. Vérifiez l'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 l'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é l'application pour accéder à la base de données « cutron01_contact_demo » : La table contact de la base de données

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
  1. Lisez le paragraphe « Creating an Entity Class »
  2. Utilisez le « MakerBundle » pour générer l'entité « Contact » conformément à la table MySQL :
    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 »
  3. Actualisez la liste des fichiers du répertoire « Entity » par un clic droit sur la racine du projet puis « 🗘 Reload from disk »
  4. Observez le fichier généré dans le répertoire « src/Entity »
  5. Consultez la classe générée et expliquez les divers attributs PHP.
  6. Observez le fichier généré dans le répertoire « src/Repository »

Liste des contacts

Travail à réaliser
  1. Créez un nouveau contrôleur « ContactController » à l'aide du « MakerBundle », sans générer les tests PHPUnit proposés
  2. Lisez le chapitre « Récupérer des objets dans la base de données » de la documentation de Symfony
  3. Supprimez tous les éléments inutiles de l'action et de la vue ainsi que les paramètres passés à la vue
  4. En paramètre de l'action « index() » générée par défaut, demandez le service « ContactRepository »
  5. 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)
  6. Transmettez l'ensemble des contacts à la vue Twig
  7. Donnez un titre au contenu Web produit et proposez un titre de niveau 1 représentatif
  8. 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 »
  9. 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
  1. Copiez le fichier d'environnement de test « .env.test » en « .env.test.local »
  2. Définissez la variable contenant le chemin d'accès à la base de données comme dans l'environnement de développement
  3. Ajoutez ce nouveau fichier à la liste des paramètres de configuration de Codeceptiona> dans le fichier de configuration de Codeception « codeception.yml »
  4. 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
  5. Générez une classe de « Cest » pour l'action « index() » du « ContactController » :
    php vendor/bin/codecept generate:cest Controller Contact\\Index
  6. Supprimez les méthodes de la classe générée (« _before() » n'est pas utile et « tryToTest() » est un exemple)
  7. Sur le modèle des tests présentés en début de TP, écrivez une méthode de test « contactListContainsRightNumberOfContacts() » permettant de contrôler que la ressource « /contact » donne une réponse HTTP 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
    Information

    Profitez des suggestions de PhpStorm au fil de la saisie du 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, ils facilitent 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
  1. Dans le contrôleur « ContactController », créez une action « show(int $contactId) » qui reçoit en paramètre un identifiant de contact
  2. Associez à la nouvelle action une route dont le chemin est « /contact/{contactId} »
  3. Appliquez les conditions requises à « contactId »
  4. Dans la nouvelle action, demandez le rendu de la vue Twig « contact/show.html.twig »
  5. Générez le fichier Twig à l'aide de PhpStorm
  6. Vérifiez la bonne interaction de ces trois éléments
  7. En paramètre de l'action « show() », demandez le service « ContactRepository »
  8. Dans l'action, récupérez le contact concerné et transmettez-le à la vue
  9. Si le contact n'existe pas, levez une « NotFoundHttpException » dont le texte doit être explicite
  10. Présentez le nom et le prénom du contact dans le titre du document HTML et dans un titre de niveau 1
  11. Présentez toutes les informations du contact (hormis l'identifiant) dans une liste de définitions
  12. Vérifiez que la route produit un résultat conforme
  13. Vérifiez que la route produit une réponse « 404 » pour un identifiant de contact qui n'existe pas
    Information

    Vous 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
  1. 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 »
  2. Modifiez les paramètres de l'action « show() » pour :
    • Remplacer l'identifiant de contact par un « Contact »
    • Supprimer le service « ContactRepository » qui sera inutile
  3. Maintenez les conditions requises sur le paramètre du chemin de la route
  4. Supprimez le code maintenant inutile de l'action, en particulier le test
  5. Vérifiez que la route produit toujours un résultat conforme
  6. 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
  1. Si nécessaire, relisez le chapitre « Linking to Pages » de la documentation
  2. Modifiez la vue « contact/index.html.twig » afin qu'elle propose à présent des liens vers les détails du contact sur chaque nom de contact
  3. Vérifiez que les liens sont correctement produits et fonctionnels
  4. Améliorez le précédent test fonctionnel pour qu'il vérifie que la liste à puces contient 195 liens
  5. É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 propre 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 devrez modifier la configuration pour accéder à votre propre base de données MySQL.

Travail à réaliser
  1. Ouvrez le fichier de la configuration locale du projet
  2. 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 serveur MySQL du département, à savoir « mariadb-10.2.25 »
      Information

      La valeur de « serverVersion » doit impérativement être correcte pour que les migrations fonctionnent. En effet, MySQL et MariaDB n'ont pas la même façon d'accéder aux méta-informations de la base de données.

  3. Lancez la création de la base de données :
    bin/console doctrine:database:create
  4. 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
  1. Lancez la construction de la première migration
    bin/console make:migration
  2. 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
  1. Lancez l'exécution des migrations
    bin/console doctrine:migrations:migrate --no-interaction
  2. Sur phpMyAdmin, vérifiez la présence, la structure et le contenu de la table « doctrine_migration_versions »
  3. Vérifiez la présence, la structure et le contenu de la table « contact »
  4. 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
  1. Utilisez Composer pour demander le bundle « orm-fixtures » pour le développement (ne pas oublier l'option « --dev » !)
  2. Observez les nouveaux fichiers à l'aide de l'onglet « Commit » de PhpStorm
  3. Effectuez un « commit » avec ces fichiers
  4. Utilisez Composer pour demander le bundle « zenstruck/foundry » pour le développement
  5. Observez les nouveaux fichiers à l'aide de l'onglet « Commit » de PhpStorm
  6. Effectuez un « commit » avec ces fichiers
  7. Parcourez la documentation du « DoctrineFixturesBundle »
  8. Lancez la création d'un générateur de données pour les contacts :
    bin/console make:fixtures ContactFixtures
  9. Observez le contenu du fichier généré et supprimez le corps de la méthode « load() » qui sera complété dans quelques instants
    Information

    Le 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.

  10. Effectuez un « commit » avec ce fichier
  11. Lisez le chapitre « Generate » et de la documentation de « Foundry »
  12. Lancez la création d'une nouvelle forge de données :
    bin/console make:factory
  13. Observez le fichier généré
  14. Effectuez un « commit » avec ce fichier
  15. Modifiez la méthode « defaults() » pour que :
  16. 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
  17. 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() »
  18. 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() »
  19. Effectuez un « commit » avec ce fichier
  20. Lisez les exemples du paragraphe « Using your Factory » de la documentation de « Foundry »
  21. Modifiez le générateur de données de « Contact » pour qu'il crée 150 « Contact » en utilisant la forge précédemment construite
    Information

    Vous 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
  1. Créez un script Composer « db » qui exécute les étapes suivantes :
    • Destruction forcée de la base de données
      bin/console doctrine:database:drop --force --if-exists
    • Création de la base de données
      bin/console doctrine:database:create
    • Application des migrations successives sans questions interactives
      bin/console doctrine:migrations:migrate --no-interaction
    • Génération des données factices sans questions interactives
      bin/console doctrine:fixtures:load --no-interaction
  2. Documentez ce script dans le fichier « composer.json »
  3. Documentez ce script dans le fichier « README.md »
  4. Puisque cela n'a pas été fait auparavant, documentez la configuration de la base de données dans « README.md »
  5. Lancez le script Composer « db »
  6. Observez le contenu de la table « contact » dans phpMyAdmin
  7. 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"
    Information

    Il 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.

  8. 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
  9. 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      
     ---- ----------- ----------- ------------------------------ 
    
  10. Vérifiez l'affichage de ces nouvelles données dans l'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
  1. Ajoutez au projet la dépendance de développement au module « codeception/module-doctrine »
    composer require --dev codeception/module-doctrine
  2. Activez le module « Doctrine » dans la configuration de la suite des tests « Controller » (fichier « tests/Controller.suite.yml »)
  3. Activez l'option « cleanup » pour que chaque test s'exécute dans une transaction, empêchant ainsi les effets de bords
  4. Ajoutez le fichier source de cet utilitaire (« Helper ») au projet en le plaçant dans le répertoire « tests/Support/Helper »
  5. 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
    Information

    Ce « Helper » permet de réinitialiser l'EntityManager entre chaque test pour éviter les effets de bords.

  6. Générez les classes « Actor » de Codeception avec le nouveau module activé :
    php vendor/bin/codecept build
  7. 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 »
    Information

    Il est classique de configurer une base de données SQLite pour les tests directement dans le fichier « .env.test ».

  8. Supprimez le fichier « .env.test.local »
    rm .env.test.local
  9. Supprimez la référence à « .env.test.local » dans « codeception.yml »
  10. 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é par Codeception
      php vendor/bin/codecept clean
    • Destruction silencieuse forcée de la base de données
      bin/console doctrine:database:drop --force --quiet --env=test
    • Création silencieuse de la base de données
      bin/console doctrine:database:create --quiet --env=test
    • Création silencieuse du schéma de la base de données
      bin/console doctrine:schema:create --quiet --env=test
    • Exécution des tests Codeception
      php vendor/bin/codecept run
  11. Documentez ce script dans le fichier « composer.json »
  12. Documentez ce script dans le fichier « README.md »
  13. Lancez le script Composer « test:codeception » et constatez qu'il produit des erreurs
  14. Mettez à jour le test de la liste des contacts en le débutant par la création de 5 « Contact » avec « ContactFactory »
  15. 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
  16. 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 famille
      Information

      Pour que le 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 » de Codeception pour récupérer la liste des contacts
    • Comparez l'ordre attendu et l'ordre constaté
  17. Vérifiez que le 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 le 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
  1. Créez un nouveau contrôleur « HomeController » à l'aide du « MakerBundle », sans générer les tests PHPUnit proposés
  2. Modifiez la route créée par défaut pour qu'elle soit associée au chemin « / »
  3. Remplacez le code de l'action pour effectuer une redirection « 307 » vers la route associée à « /contact »
  4. 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 à l'application en utilisant les contenus fournis par un « CDN ».

Travail à réaliser
  1. Lisez la documentation sur le démarrage rapide Bootstrap 5
  2. Modifiez le « template » de base pour y intégrer le CSS et le JavaScript de Bootstrap 5
  3. Profitez-en pour définir le « viewport » adapté
  4. 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
  1. Découvrez comment fonctionne la « Navbar » Bootstrap en lisant la documentation
  2. Copiez l'exemple de la documentation dans le modèle Twig de base
  3. Conservez les éléments suivants :
    1. le lien « navbar-brand » qui contient le texte « Contacts » et mène à la page d'accueil
    2. l'élément « navbar-collapse » qui correspond au menu complet ou au menu hamburger selon la taille de la fenêtre
    3. une liste « navbar-nav » qui ne contiendra que le second élément « nav-item » non modifié
    4. le formulaire de recherche que vous franciserez
  4. Vérifiez que la barre de navigation s'affiche correctement, notamment sous forme d'un menu hamburger en mode mobile

Ajout d'un pied de page

Il est classique d'intégrer les informations légales en bas de page. Ceci demande la création d'un pied de page dans le « template » de base.

Travail à réaliser
  1. Ajoutez un pied de page dans le « template » de base
  2. Le pied de page doit contenir le texte « MyContacts - © 2025 » dans un élément HTML structurant

Présentation générale de l'application

L'interface de l'application occupe toute la largeur de la fenêtre du navigateur. Vous allez utiliser les « container » de Bootstrap pour centrer le contenu et lui donner une largeur maximale adaptée à la taille de l'écran.

Travail à réaliser
  1. Découvrez comment fonctionnent les « Containers » Bootstrap
  2. Limitez la largeur d'affichage en affectant la classe « container-xxl » à l'élément HTML « body »
  3. Découvrez comment fonctionnent les « margin » et « padding » Bootstrap
  4. Supprimez le « padding » par défaut de l'élément HTML « body »
  5. Encadrez le bloc Twig « body » avec un élément HTML sémantique représentant le contenu principal de la page
  6. Le bloc Twig « body » ayant perdu son sens inital, il convient de le renommer en « main » :
    • Lisez le principe de réusinage/refactorisation de code expliqué par Martin Fowler
    • Placez le curseur dans le mot « body » du bloc Twig « body »
    • Utilisez le menu de renommage (SHIFT+F6) pour modifier le nom « body » en « main »
    • Validez la modification
    • Observez que le nom a été modifié dans toutes les vues qui utilisaient ce bloc
  7. Définissez la marge supérieure du contenu principal au « niveau 3 » pour qu'il soit espacé de la barre de navigation
  8. Définissez les marges horizontales du contenu principal au « niveau 3 » pour qu'il ne colle pas aux bords de la fenêtre, seulement à partir de la largeur « lg »
  9. Définissez la marge inférieure du contenu principal au « niveau 5 » pour qu'il soit espacé du pied de page
  10. Découvrez comment fonctionnent les alignements de texte Bootstrap
  11. Centrez le texte du pied de page
  12. Ajoutez du « padding » « niveau 3 » au pied de page
  13. Découvrez comment fonctionnent les fonds Bootstrap
  14. Affectez un fond « dark » au pied de page
  15. Définissez l'opacité du fond du pied de page à 25 %

Gestion des « assets », feuille de style et icône 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
  1. Lisez la documentation « AssetMapper: Simple, Modern CSS & JS Management » jusqu'à la fin du paragraphe installation
  2. Accédez à la page d'accueil de l'application
  3. Observez le code source HTML reçu par le navigateur (CTRL+U)
  4. Constatez qu'une feuille de style est intégrée
  5. Mettez provisoirement en commentaire la ligne responsable de l'intégration de la feuille de style dans le « template » de base :
    {# {% block importmap %}{{ importmap('app') }}{% endblock %} #}
  6. Actualisez le code source de la page reçu par le navigateur
  7. Observez qu'il n'y a plus de feuille de style
  8. Supprimez le commentaire de la ligne qui intègre cette feuille de style dans le « template » de base pour revenir à l'état initial :
    {% block importmap %}{{ importmap('app') }}{% endblock %}
  9. D'après la documentation « AssetMapper: Simple, Modern CSS & JS Management » que vous avez lue, trouvez la feuille de style dans l'arborescence du projet
  10. Modifiez la couleur de fond dans la feuille de style
  11. 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 classes CSS « firstname » et « lastname »
  12. Complétez la feuille de style pour que les éléments de la classe « lastname » soient en gras et en petites majuscules
  13. Complétez la feuille de style pour que les éléments de la classe « firstname » soient en italic
  14. Lisez le chapitre « Linking to CSS, JavaScript and Image Assets »
  15. Placez l'icône de site suivante dans le répertoire des « assets » : icône personnalisée
  16. Intégrez cette icône dans le « template » de base en utilisant la fonction asset() de Twig
  17. Vérifiez que l'icône s'affiche dans l'onglet du navigateur
  18. Observez le code HTML correspondant dans le code source de la page reçu par le navigateur
  19. Observez la sortie de la commande suivante :
    php bin/console debug:asset-map --full
  20. Trouvez les lignes correspondant à la feuille de style et à l'icône
  21. 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 l'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.

    N'utilisez donc JAMAIS la commande qui suit en mode développement !

    php bin/console asset-map:compile

Gestion fine des blocs de contenu Twig

Les blocs Twig sont des espaces réservés au sein des « templates » et leur contenu peut être remplacé. Une utilisation astucieuse des blocs permet de rendre les « templates » plus flexibles et réutilisables. Dans l'application, le titre (élément HTML « title ») de chaque page est classiquement identique au titre principal (élément HTML « h1 »). Vous allez modifier le « template » de base pour que le titre de chaque page (élément HTML « title ») soit repris automatiquement comme titre principal (élément HTML « h1 »), mais puisse également être différent si besoin.

Travail à réaliser
  1. Lisez la documentation de la fonction « block() » de Twig
  2. Modifiez le « template » de base pour que le contenu du bloc « title » soit repris dans un élément HTML « h1 » placé dans le contenu principal
  3. Ajoutez le texte « MyContacts -  » au titre de chaque page en le plaçant avant le contenu du bloc Twig « title »
  4. Lisez la documentation du tag Twig « apply »
  5. Lisez la documentation du filtre Twig « striptags »
  6. Modifiez le « template » de base pour que le contenu du bloc « title » soit nettoyé de toute balise HTML dans l'élément HTML « title »
  7. Vérifiez que le titre de la page d'accueil s'affiche correctement
  8. Modifiez les diverses vues pour supprimer les éventuels doublons de titre

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
  1. Vérifiez que les tests ne passent plus
  2. Ajoutez la classe CSS « contacts » à la liste des contacts
  3. Mettez le sélecteur des tests en accord avec cette modification
  4. 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
  1. Créez une nouvelle méthode « search() » dans le « ContactRepository »
  2. Ajoutez-lui un paramètre représentant le texte cherché ainsi qu'un type de retour « array »
  3. Ajoutez une annotation pour préciser le type de retour « Contact[] »
  4. Lisez la partie « Querying with the Query Builder » la documentation de Doctrine et découvrez les possibilités offertes par le « QueryBuilder »
  5. 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
  1. Ouvrez le fichier du « ContactController » et placez-vous au niveau de la méthode « index() »
  2. Remplacez l'utilisation de la méthode « findBy() » du « ContactRepository » par la nouvelle méthode « search() »
  3. Vérifiez que l'affichage fonctionne toujours
  4. Demandez l'objet « Request » (correspondant à la requête HTTP) en le déclarant dans les paramètres de la méthode
  5. Lisez comment utiliser l'objet « Request »
  6. Récupérez la valeur du paramètre HTTP GET « search » ou une chaîne vide par défaut
  7. Utilisez la valeur précédente comme texte de recherche des contacts
  8. Ajoutez le paramètre HTTP GET « search » dans l'URL de listage des contacts (avec comme valeur « le », par exemple)
  9. 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
  1. Fixez le nom du champ de saisie pour qu'il corresponde au code du contrôleur
  2. Testez le champ et le bouton de recherche du formulaire
  3. Consultez l'URL « /contact/12 » puis utilisez le champ de recherche depuis cette URL
  4. Ajoutez une action au formulaire pour que la route « app_contact » soit utilisée
  5. Constatez que la valeur cherchée n'apparaît pas dans le champ de recherche lors de l'affichage des résultats
  6. Dans le contrôleur, passez la valeur de « search » à la vue
  7. Donnez la valeur de « search », ou par défaut une chaîne vide, comme valeur du champ de recherche du formulaire
  8. Vérifiez le comportement de l'ensemble
  9. Complétez la vue pour que le titre de la page « Liste des contacts » soit complété par « contenant <texte cherché> » si un texte est cherché
  10. Encadrez la précédente valeur du texte cherché avec un élément HTML « em » pour la mettre en évidence, en lui affectant la classe CSS « text-secondary »
  11. Observez le code source HTML reçu par le navigateur (CTRL+U) :
    • Vérifiez que l'élément HTML « h1 » est correct
    • Vérifiez que l'élément HTML « title » est correct, en particulier qu'il ne contient pas de balise HTML

Test de la fonctionnalité

La pérennité de cette fonctionnalité passe nécessairement par un test.

Travail à réaliser
  1. Écrivez une nouvelle méthode de tests « searchFormCanBeFilledAndSent() » dans « tests/Controller/Contact/IndexCest.php » :
  2. Écrivez une nouvelle méthode de tests « searchResultRestrictsContactList() » 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, un autre dans le prénom et un troisième dans le nom et le prénom
    • Lancez la recherche de la séquence de caractères directement avec l'URL correspondante
    • Vérifiez que la réponse est un succès
    • Capturez le contenu de la liste des contacts
    • Vérifiez que la capture contient les 3 contacts attendus
    • Vérifiez que le champ de recherche contient la valeur cherchée
  3. Vérifiez que les tests passent

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
  1. Dans la méthode « search() » du « ContactRepository », effectuez une recherche uniquement si le texte cherché n'est pas vide
  2. Dans le contrôleur, remplacez l'utilisation de l'objet « Request » par l'utilisation de l'attribut PHP « MapQueryParameter » pour obtenir la valeur du paramètre « search »
  3. Vérifiez que le 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
  1. 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
  2. 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
  1. Utilisez le « MakerBundle » pour modifier l'entité « Contact » existante :
    bin/console make:entity Contact
  2. 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
  3. Ajoutez une propriété « category » de type relation « ManyToOne » qui peut être nulle et qui impliquera une propriété « contacts » dans l'entité « Category »
  4. Observez le code de « Category » ainsi que les modifications du code de « Contact »

Migration

Vous savez que toute modification des entités implique de modifier la structure de la base de données et doit être référencée dans une migration.

Travail à réaliser
  1. Créez une nouvelle migration
  2. Vérifiez le code de la migration
  3. Constatez que l'affichage de la liste des contacts ne fonctionne plus
  4. Appliquez la migration
  5. 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
  1. Tentez de re-générer la base de données en utilisant le script « Composer »
  2. Mettez à jour la forge de « Contact » pour corriger le problème
  3. Vérifiez que vos contacts ont des numéros de téléphone français valides
  4. Générez la forge pour la classe « Category »
  5. 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
  6. Créez un générateur de données factices pour la classe « Category »
  7. 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
  8. 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 »
    Information

    Vous 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.

  9. Générez une nouvelle version de la base de données et constatez l'apparition des catégories
  10. Indiquez les dépendances entre « ContactFixtures » et « CategoryFixtures »
  11. Modifiez le générateur de « Contact » pour qu'il affecte une catégorie aléatoire à 90% des contacts générés
    Information

    La méthode « boolean() » de Faker admet un paramètre de type « integer » qui fixe la probabilité d'obtenir « true » sur 100.

    Vous aurez besoin de la fonction « faker() » qui vous donnera accès à une instance de Faker.

    Puisque le tirage aléatoire de catégorie doit avoir lieu pour chaque entité « Contact » générée, vous devrez utiliser une fonction anonyme. Vous pouvez vous appuyer sur l'exemple fourni dans « Using with DoctrineFixturesBundle ».

  12. Générez encore une fois une nouvelle version de la base de données
  13. 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"
    
  14. Utilisez le générateur « AppFixtures » pour générer une catégorie aléatoire qui ne contiendra aucun contact
  15. Indiquez les dépendances entre « AppFixtures » d'une part et « ContactFixtures » et « CategoryFixtures » d'autre part
    Information

    Vous devez imposer les dépendances pour que la catégorie construite dans « AppFixtures » reste vide. Elle doit donc être construite en dernier.

  16. 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
  1. 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 Twig « ?? » 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"
  2. Créez un contrôleur « CategoryController » pour les catégories
  3. Utilisez l'action générée par défaut pour effectuer un listage alphabétique des catégories mis en forme avec Bootstrap
    Information

    Pensez à supprimer tous les éléments autogénérés inutiles dans le contrôleur et la vue.

  4. Exploitez l'entrée de menu inutilisée de la « Navbar » pour proposer un lien « Catégories » vers la liste des catégories
  5. Modifiez la feuille de style de l'application pour alterner les couleurs de fond des éléments de la liste des catégories entre la couleur de base et la couleur « light » de Bootstrap en utilisant la variable CSS « --bs-light »

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
  1. Lisez le chapitre « Including Templates » de « Reusing Template Contents »
  2. Créez un « template fragment » « templates/contact/_contacts.html.twig » qui contient uniquement le code HTML d'affichage d'une liste de contacts extrait de la vue « templates/contact/index.html.twig »
  3. Modifiez la vue « templates/contact/index.html.twig » pour inclure ce « template »
  4. Vérifiez l'affichage de la liste et lancez les tests
  5. Dans le contrôleur « CategoryController », créez une nouvelle action « show() » qui liste les contacts d'une catégorie :
    Information

    Pensez à passer une variable « contacts » en paramètre de l'inclusion du « template fragment ».

  6. Sur le modèle de la liste des catégories, modifiez l'affichage de la liste des contacts en utilisant les listes Bootstrap

Derniers détails

Il reste quelques détails d'affichage à régler.

Travail à réaliser
  1. Consultez la ressource « /category/1 » pour vérifier votre travail
  2. Constatez que les contacts d'une catégorie ne sont pas triés
  3. Ajoutez un critère de tri sur la collection des contacts de la catégorie (nom puis prénom)
  4. Dans la liste de catégories, ajoutez un lien vers les contacts de la catégorie
    Information

    Vous pouvez utiliser les classes Bootstrap « nav » et « nav-link » pour faire un lien dans une liste.