TP n°4 - Programmation contrôleur Web Symfony
Code applicatif PHP déclenché en réponse aux requêtes HTTP

Table des matières

1. Introduction

Cette séance vise à expérimenter la façon dont les programmes PHP sont invoqués côté serveur pour traiter les requêtes émises par les clients.

On observera la mise en œuvre qui a été faite sur l’application « fil-rouge » ToDo dans le framework Symfony, avec un serveur PHP s’exécutant en local.

2. Étape 1 : Exécution d’une application Web Symfony

Cette étape permet de se familiariser avec la façon dont on peut tester le fonctionnement d’une application Symfony, en contexte Web, grâce à un serveur HTTP local.

Dans la version préparée en amont du cours, dont vous êtes partis, l’application « fil-rouge » ToDo dispose en effet, d’une interface pour HTTP, en plus de l’interface en ligne de commande que nous avons utilisée abondamment.

2.1. TODO Étape 1-a : Lancement du serveur HTTP local sur l’application ToDo

Démarrer le serveur Web local intégré à l’environnement de développement Symfony, pour pouvoir tester les interactions HTTP en local.

Lancez le serveur Web, comme déjà vu dans les séances précédentes :

cd $HOME/CSC4101/tp-02/todo-app/
symfony server:start

Dans la suite de la séance, vous allez tester l’exécution de l’application « fil rouge » ToDo dans un contexte Web.

Un serveur HTTP est lancé par l’utilitaire en ligne de commande Symfony. Il fonctionne grâce à un serveur HTTP embarqué par PHP.

Il permet de tester en local l’exécution du code de l’application Symfony présente dans le répertoire dans lequel il a été démarré.

L’application Web s’exécutera en « boucle », réagissant en réponse aux requêtes transmises à travers le serveur HTTP local.

L’intérêt en environnement de développement est que si vous mettez à jour le code, ces mises à jour seront prises en compte sans avoir à relancer le serveur HTTP.

Gardez le terminal ouvert avec le serveur lancé dedans, pour pouvoir consulter les logs sur la sortie affichée dans le terminal, au fur et à mesure de la mise-au-point.

2.2. TODO Étape 1-b : Affichage des informations de mise-au-point dans la barre d’outils Symfony

Explorer les fonctionnalités de la barre d’outils Symfony facilitant la mise-au-point au niveau du routage des requêtes vers les contrôleurs.

  1. Consultez la page affichée pour http://localhost:8000/ dans le navigateur.

    Remarquez la « barre d’outils » située en bas de page.

    symfony-toolbar.png

    Figure 1 : Barre d’outils du développeur Symfony

  2. Vérifiez ce qui se passe quand on place la souris au-dessus du code de réponse (404)

    Remarquez le Route name « n/a » affiché dans le « pop-up »

  3. Cliquez sur cette case avec le code de retour. Une page s’affiche qui donne des détails sur le contexte d’exécution Symfony qui a donné lieu à la réponse HTTP en question/

    Examinez la page du « Profiler Symfony » qui s’affiche.

    symfony-profiler-view.png

    Figure 2 : Page du « Symfony Profiler » dans les outils du développeur Web

    Identifiez les informations de mise-au-point utiles, expliquant le contexte détaillé de la requête HTTP en question.

2.3. TODO Étape 1-c : Accès réussi à une ressource de l’application ToDo

Examiner le comportement de l’application Symfony visible dans les outils du développeur Symfony, quand une requête HTTP est réussie.

  1. Naviguez sur l’URL http://localhost:8000/todo/

    Une page d’accueil sommaire de l’application s’affiche. Quel est le code de réponse de la réponse HTTP ?

    Vérifiez ce code aussi bien :

    • dans les traces du serveur sur sa sortie standard dans le terminal,
    • que dans la barre d’outils de Symfony affichée en bas de page.
  2. Cliquez sur la case du code de retour « 200 » en bas à gauche de la barre d’outils Symfony

    Constatez les informations affichées dans la page « Request / Response » qui s’affiche, comme :

    le lien « TodoController::indexAction »
    nom du point d’entrée Symfony exécuté en réponse à la requête
    les « Request Headers »
    les en-têtes de la requête envoyés par le navigateur comme host, que vous connaissez
    le paramètre DATABASE_URL dans les « Server Parameters / Defined in .env »
    la variable de connexion à la base de données que vous connaissez aussi
  3. Cliquez sur le menu « Routing » à gauche

    Remarquez le nom de la « route » correspondant à cette requête : home et la correspondance avec le chemin d’URL /todo/.

    L’application Symfony maintient une table de routage des chemins d’accès aux ressources, et des codes PHP gestionnaires.

    Ici, la requête est réussie (code de retour 200) car on est sur une route connue.

    Le code du contrôleur Symfony gérant cette route qui a été invoqué est dans la méthode App\Controller\TodoController::indexAction(), que nous verrons plus loin, dans le code PHP.

  4. Essayez de naviguer cette fois sur l’URL http://localhost:8000/hello

    Examinez le code de retour et les informations de routage affichées. Est-ce cohérent ?

2.4. TODO Étape 1-d : Examen de la table de routage de ToDo

On va examiner le mécanisme de la table de routage qui permet de définir quel composant de l’application Symfony écrit par le développeur doit être exécuté en réponse aux requêtes HTTP.

Dans la séance précedente, on avait vu que le code de l’application pouvait être exécuté au moyen des classes de son interface en mode « ligne de commande », gérant des sous-commandes app:... de symfony console, pour afficher des informations sur la sortie standard, par exemple.

Par exemple, la commande symfony console app:list-todos invoquait l’exécution de la méthode App\Command\ListTodosCommand::execute présente dans le fichier PHP src/Command/ListTodosCommand.php.

Ici, en environnement Web, le code de l’application est exécuté en réponse aux requêtes HTTP, via l’invocation par PHP du moteur du noyau Symfony. Symfony appelle une méthode de classe Contrôleur en fonction de sa table de routage.

Examinez la table de routage Web de l’application ToDo :

symfony console debug:router
--------------------------- -------- -------- ------ ----------------------------------- 
  Name                        Method   Scheme   Host   Path                               
 --------------------------- -------- -------- ------ ----------------------------------- 
  _preview_error              ANY      ANY      ANY    /_error/{code}.{_format}           
  _wdt                        ANY      ANY      ANY    /_wdt/{token}                      
  _profiler_home              ANY      ANY      ANY    /_profiler/                        
  _profiler_search            ANY      ANY      ANY    /_profiler/search                  
  _profiler_search_bar        ANY      ANY      ANY    /_profiler/search_bar              
  _profiler_phpinfo           ANY      ANY      ANY    /_profiler/phpinfo                 
  _profiler_xdebug            ANY      ANY      ANY    /_profiler/xdebug                  
  _profiler_font              ANY      ANY      ANY    /_profiler/font/{fontName}.woff2   
  _profiler_search_results    ANY      ANY      ANY    /_profiler/{token}/search/results  
  _profiler_open_file         ANY      ANY      ANY    /_profiler/open                    
  _profiler                   ANY      ANY      ANY    /_profiler/{token}                 
  _profiler_router            ANY      ANY      ANY    /_profiler/{token}/router          
  _profiler_exception         ANY      ANY      ANY    /_profiler/{token}/exception       
  _profiler_exception_css     ANY      ANY      ANY    /_profiler/{token}/exception.css   
  app_admin_dashboard_index   ANY      ANY      ANY    /admin                             
  home                        GET      ANY      ANY    /todo/                             
  todo_list                   GET      ANY      ANY    /todo/list                         
  todo_index                  GET      ANY      ANY    /todo/index                        
  todo_show                   GET      ANY      ANY    /todo/{id}                         
 --------------------------- -------- -------- ------ ----------------------------------- 

Ainsi, l’accès par le client HTTP à l’URL http://localhost:8000/todo/ entraîne le traitement de la requête HTTP GET (correspondant à « Method » valant GET) sur la ressource /todo/ (« Path » valant /todo), qui peut donc être routée vers la route nommée home.

Si on demande plus de détails dans cette commande, avec symfony console debug:router --show-controllers la colonne « Controller » nous affiche, en face de cette route home le nom de la méthode qui sera appelée par le routeur Symfony : App\Controller\TodoController::indexAction().

On va voir que cette méthode est celle d’une classe Contrôleur Symfony (codée dans le fichier src/Controller/TodoController.php).

3. TODO Étape 2 : Examen du code du contrôleur HTTP de ToDo

On va maintenant examiner la façon dont le programmeur peut configurer les informations de routage et programmer le code PHP exécuté en réponse aux requêtes HTTP.

  1. Ouvrez le code du contrôleur Web Symfony src/Controller/TodoController.php dans l’IDE.

    Note : vous pouvez également consulter son code depuis le profileur Symfony en cliquant sur le lien « TodoController::indexAction » dans la page du menu « Request / Response ».

  2. Observez les annotations Route présentes dans le code, à l’aide d’attributs PHP 8 :

    ...
    use Symfony\Component\Routing\Annotation\Route;
    ...    
    
    #[Route('/todo')]
    class TodoController extends AbstractController
    {    
        ...
    

    ceci définit un préfixe qui s’appliquera à l’ensemble des routes des méthodes de toute la classe.

    puis :

    #[Route('/', name: 'home', methods: ['GET'])]
    public function indexAction()
    {
    

    ainsi, indexAction() est associé au chemin de route '/todo' + ’/’ : ’/todo/’, et au nom de route home.

    Vous retrouvez les informations présentes pour le chemin (« Path »), dans la table de routage.

  3. Examinez le code de la méthode TodoController::indexAction()

    Le code est relativement simple :

    ...
    
    use Symfony\Component\HttpFoundation\Response;
    
    ...
    
    $htmlpage = '<!DOCTYPE html><html>....</html>';
    
     return new Response(
         $htmlpage,
         Response::HTTP_OK,
         array('content-type' => 'text/html')
     );
    
    1. définition d’un contenu HTML à renvoyer en réponse à la requête
    2. construction d’une réponse HTTP avec les propriétés suivantes :
      • contenu de la réponse (HTML)
      • code de réponse HTTP OK (en fait une constante Response::HTTP_OK qui vaut 200)
      • une liste d’en-têtes de réponse : ici la définition explicite du type HTML pour le contenu renvoyé (Content-type)

Vous retrouvez les composants essentiels d’une réponse HTTP : code de réponse, en-têtes et contenu.

Ce code est très simple, et n’est pas représentatif de ce qu’on effectue d’ordinaire dans un contrôleur d’application Symfony, mais nous l’avons construit pour les vertus de l’exemple. Ce n’est pas hyper élégant, mais assez explicite… et ça marche.

Nous n’avons pas encore étudié le langage HTML, donc il se peut que le contenu de la réponse ne vous semble pas trivial à ce stage. Nous l’étudierons plus en détail dans une prochaine séance (et verrons comment générer du code HTML plus proprement, avec les gabarits).

3.1. TODO Test des routes existantes

Vérifiez que les routes listées ci-dessus sont bien accessibles à partir d’un navigateur :

  • Liste des tâches : http://localhost:8000/todo/list
  • Affichage d’une tâche : http://localhost:8000/todo/1

Comparez la sortie de la commande debug:route, testez les URL correspondantes et consultez le code du contrôleur Web.

Les routes commençant par un underscore, comme _profiler sont des routes spéciales liées au fonctionnement des outils de développement de Symfony, comme le Profiler qu’on a utilisé un peu plus tôt. Elles n’existent qu’en environnement de développement, indépendamment du code de notre application présent dans src/Controller/.

Vous comprenez maintenant comment est déclaré dans le code de la partie Routage de la gestion des requêtes HTTP dans une application Symfony.

4. Étape 3 : Ajout d’un Contrôleur Web, dans Todo, pour les étiquettes

Le but de cette étape est d’ajouter un contrôleur Web dans l’application Todo

On peut écrire du code d’une classe PHP dans src/Controller/ ou bien utiliser un assistant générateur de code de Symfony : make:controller. Il génère pour nous une nouvellle classe Contrôleur, et les éléments associés.

4.1. Étape 3-a : Ajout d’une classe TagController

Vous allez utiliser l’assistant générateur de code make:controller pour ajouter de quoi consulter les étiquettes (Tag) dans l’application Todo.

On adaptera ensuite le code généré, sur le modèle de la classe TodoController.

Procédez aux étapes suivantes :

  1. utilisez l’assistant make:controller pour générer le code d’une classe TagController :

    symfony console make:controller TagController
    
    created: src/Controller/TagController.php
    created: templates/tag/index.html.twig
    
    
     Success! 
    
    
    Next: Open your new controller class and add some pages!
    
  2. allons-y, et ouvrons le fichier source généré src/Controller/TagController.php dans l’IDE.

    Rechargez le contenu affiché dans le panneau d’arborescence des fichiers : on a généré de nouveaux fichiers, mais l’IDE ne s’en est probablement pas rendu compte.

    Vous constatez que le code est plutôt concis.

    L’assistant a généré également un fichier de gabarit Twig : templates/tag/index.html.twig, qui est utilisé, dans ce code, pour générer le contenu d’une page Web.

    Vous étudierez prochainement les gabarits Twig, donc on ne va pas aller voir le détail de ce gabarit pour l’instant.

  3. consultez la table de routage de l’application :

    symfony console debug:router
    

    Elle contient maintenant la nouvelle route app_tag accessible sur /tag, ce qui est conforme à la déclaration des attributs de la méthode index() dans le code du contrôleur (#[Route('/tag', name: 'app_tag')]).

  4. lancez le serveur Web :

    symfony server:start
    

    Testez l’URL http://localhost:8000/tag

    Vous obtenez sans surprise la page « Hello TagController! » dont le contenu est spécifiée par le gabarit templates/tag/index.html.twig.

4.2. Étape 3-b : Utilisation de dump() en contexte Web

  1. Modifiez le code de la méthode TagController::index() pour ajouter un appel à dump() :

    #[Route('/tag', name: 'app_tag')]
    public function index(): Response
    {
        dump("hello world");
    
        return $this->render('tag/index.html.twig', [
            'controller_name' => 'TagController',
        ]);
    }
    
  2. sans avoir besoin de relancer le serveur Web, rechargez juste la page /tag affichée dans le navigateur.

    Vous devez voir apparaître dans la barre d’outils Symfony en bas de page, une icône « cible » qui donne accès à l’affichage des dumps (il suffit de passer la souris dessus pour voir les messages en pop-ups).

    dump-popup.png

    Figure 3 : Affichage d’un pop-up pour les valeurs des dump()

L’utilisation de dump() est très utile pour la mise-au-point quand on teste en mode Web.

Cela permet de mettre des affichages d’infos pour déboguer un peu partout dans le code, sans polluer l’affichage des pages, puisque les infos de dump sont cachées dans ce sous-menu de la barre d’outils Symfony.

On peut maintenant passer au codage des traîtements à effectuer pour afficher une liste d’étiquettes.

4.3. Étape 3-c : Ajout de l’affichage des étiquettes

Nous pouvons maintenant ajouter une méthode destinée à charger et afficher toutes les étiquettes.

Le chargement se fera depuis la base de données avec Doctrine, comme pour ce qu’on sait déjà faire dans l’exécution des commandes en mode console.

  1. Ajoutez la méthode suivante :

    use App\Repository\TagRepository;
    
    // ...
    
        /**
         * Lists all tags entities.
         */
        #[Route('/tag/list', name: 'tag_list', methods: ['GET'])]
        public function listAction(TagRepository $tagRepository)
        {
            $htmlpage = '<!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>tags list!</title>
        </head>
        <body>
            <h1>tags list</h1>
            <p>Here are all your tags:</p>
            <ul>';
    
            $tags = $tagRepository->findAll();
            foreach($tags as $tag) {
                $htmlpage .= '<li>'. $tag->getName() .'</li>';
            }
            $htmlpage .= '</ul>';
    
            $htmlpage .= '</body></html>';
    
            return new Response(
                $htmlpage,
                Response::HTTP_OK,
                array('content-type' => 'text/html')
                );
        }
    

    La génération du HTML est inspirée de ce qui est fait dans TodoController::listAction() (on verra plus tard comment faire mieux avec des gabarits).

    Quand à la connexion avec la base de données, on diffère un peu du code de TodoController : on utilise ici le mécanisme de « cablage » automatique (autowiring) des contrôleurs avec les repositories de Doctrine.

    Ce mécanisme est présenté dans le deuxième exemple de la documentation Fetching Objects from the Database :

    Another possibility is to use the ProductRepository using Symfony’s autowiring and injected by the dependency injection container.

    le conteneur d’injection de dépendance dont il est question ici, c’est le mécanisme qui permet de passer un repository (TagRepository $tagRepository) en argument de notre méthode listAction().

    Testez sur l’URL /tag/list que cela affiche bien les tâches présentes dans la base de données.

  2. Cliquez sur l’icône représentant un « cylindre » à 3 couches en bas de page dans la barre d’outils du développeur Symfony.

    Vous aboutissez à la page « Doctrine » des outils de mise au point, qui permet de voir la requête SELECT qui a été générée par Doctrine lors de l’appel à findAll().

Voilà pour la création d’un contrôleur sur l’application Todo. Vous pouvez maintenant passer à l’ajout de contrôleurs dans votre projet.

5. TODO Étape 4 : Ajout d’un Contrôleur Web dans le projet

Vous allez maintenant pouvoir démarrer l’ajout d’un contrôleur Web dans votre projet

Vous commencez maintenant cette mise en œuvre, en vous référant au Guide de réalisation du projet.

Les instructions sont données dans la section « Création des premières pages publiques, en consultation » pourvu que vous ayez suffisamment avancé sur les précédentes tâches du projet.

Vous la continuerez en travail hors présentiel sur votre projet, d’ici la prochaine séance de TP (Notez le label « Après_TP_4 » dans la section).

6. Évaluation

À la fin de cette séquence, vous savez :

  • utiliser des outils de la barre d’outils Symfony pour examiner le fonctionnement de l’application côté serveur
  • identifier comment le code PHP est appelé en réponse aux requêtes HTTP dans une application Web Symfony, via les contrôleurs
  • examiner la table de routage d’une application Symfony
  • générer le code d’une classe contrôleur avec make:controller
  • afficher des informations de déboguage avec dump()
  • cabler automatiquement un contrôleur avec Doctrine pour permettre l’exécution de requêtes en base de données
  • examiner les requêtes SQL exécutées sous le capot

7. TODO Ensuite, d’ici la semaine prochaine

D’ici le prochain cours magistral, vous allez travailler en hors-présentiel, sur l’apprentissage de HTML et Twig, pour la vous initier à la fabrication des pages HTML.

Vous vous appuierez sur le support de travail hors-présentiel 4-5 pour cela.

Author: Olivier Berger (TSP)

Date: 2024-09-27 Fri 13:09

Emacs (Org mode)