TP n°1 - Prise en main de PHP et de la boîte à outils Symfony
Application PHP en ligne de commande « gestionnaire de tâches Todo »

Table des matières

1. Introduction

Cette séance permet de familiariser les étudiants avec l’environnement de programmation PHP et de mise au point basé sur Symfony, pour l’écriture de programmes interactifs en PHP lancés en ligne de commande.

Dans cette séance, vous commencerez l’étude d’une application de gestion de tâches fournie, appelée ToDo, programmée en PHP avec le framework Symfony. Elle servira de fil rouge tout au long des TPs, pour étudier la mise en œuvre des concepts abordés dans le cours.

Un squelette d’application ToDo sera fourni pour démarrer cette première mise en œuvre. Il contient déjà de nombreuses fonctionnalités, même si nous étudierons uniquement certaines d’entre-elles dès cette première séance de TP.

Pour l’instant on va s’intéresser aux mécanismes qui permettent à l’application d’accéder en lecture à une base de données relationnelle, et d’afficher le résultat de requêtes sur la sortie standard.

Cette application sera utilisable en ligne de commande, et illustre principalement le fonctionnement de la couche d’accès aux données d’une application Symfony codée en PHP objet, via le composant Doctrine.

Les étudiants vont enchaîner une vingtaine d’opérations permettant de se familiariser avec les outils du cadriciel Symfony qui vont servir pendant toute la durée des séances.

Même si l’essentiel du travail peut être réalisé en copier/coller (pour simplifier dans cette première séance), il sera utile de rester attentif au rôle des différentes manipulations, pour être capable de les reproduire ultérieurement.

N’hésitez pas à prendre des notes dans un fichier que vous conservez d’une séance à l’autre.

La séance de travail suivante permettra d’approfondir les fonctionnalités de Doctrine, et mettre en œuvre l’accès en modification dans la base de données.

L’étude du code PHP va concerner pour l’instant seulement les fonctionnalités du cadriciel Symfony pour la ligne de commande. Le fonctionnement des composants liés au Web sera abordés dans des séances ultérieures.

1.1. Prérequis

On considère que vous avez acquis les notions de base sur la syntaxe du langage PHP, dans la séquence de Travail en Autonomie qui précède cette séance.

Du point de vue théorique, cette séance revisite principalement les acquis des cours d’informatique de l’année passée, sur le modèle objet et les bases de données relationnelles.

Vous devez disposer des outils nécessaires sur votre machine BYOD, ou travailler sur une machine DISI distante.

Vous avez dû préalablement installer et configurer ces outils, grâce au support de la séquence de travail hors-présentiel qui précède cette séance (cf. wiki « Installation de l’environnement BYOD » dans l’espace Moodle du cours).

Dans cette séance, on utilisera les outils suivants :

  • la ligne de commande dans un terminal, avec le shell bash
  • l’outil Symfony en ligne de commande
  • l’environnement d’exécution PHP 8
  • Composer
  • l’IDE Eclipse (ou un équivalent) pour PHP
  • le navigateur Web

1.2. Format de ce document

Le présent support de séance de TP est consultable via une mise en forme avec l’habillage org-html-themes, qui permet de masquer/révéler les différentes sections du support1.

Des raccourcis clavier (indiqués dans un cartouche en bas à droite) permettent de naviguer directement dans les différentes tâches (TODO) à réaliser, pour aider à se concentrer sur l’avancement pas-à-pas.

2. Étape 1 : Positionnement dans l’environnement de travail personnel

On suppose que cette séance de travail s’effectue dans l’environnement personnel (BYOD), mais elle peut également être effectuée dans l’environnement des machines de salles de TP d’informatique.

2.1. Contexte : Système d’exploitation de référence

Dans le présent support de TP, les copies d’écrans et autres spécificités reflètent autant que possible l’état des versions installées sur les machines DISI des salles de TP en environnement GNU/Linux Ubuntu.

Il est probable que sur les machines individuelles, le support de PHP et de Symfony sera parfois différent.

Même si les technologies utilisées pour les séquences d’apprentissage actif (programmation PHP avec le framework Symfony) sont théoriquement portables et utilisables sur différents systèmes d’exploitation, des différences existent qui rendent parfois difficile le support au vu des différentes variantes possibles, et pour un module enseigné en promo entière.

L’équipe pédagogique ne sera pas en mesure de garantir le support relatif aux outils pour tous les systèmes d’exploitation, mais nous ferons de notre mieux pour vous accompagner.

Pour cette séance, on supposera que vous travaillez sur la machine que vous allez utiliser pour le reste des séances de TP et de travail hors-présentiel (y compris le projet).

Vous aurez besoin d’un terminal en ligne de commande, mais aussi de l’environnement graphique pour utiliser l’IDE.

2.2. TODO Étape 1-a : Création du répertoire de travail

Nous vous suggérons de travailler, pour chaque séance, dans un répertoire spécifique.

  • $HOME/CSC4101/
    • tp-01/
    • tp-02/

Nous donnons les commandes à exécuter pour un shell bash, typiquement en environnement GNU/Linux, ou via git-bash installé sur Windows.

Adaptez ces commandes à votre environnement personnel.

  1. Configurez un répertoire de travail $HOME/CSC4101/ dans votre compte.

    mkdir $HOME/CSC4101/
    cd $HOME/CSC4101/
    
  2. Pour l’instant, on crée un nouveau répertoire spécifique à cette séance :

    mkdir -p tp-01/
    cd tp-01/
    

3. Étape 2 : Récupération du code de l’application « fil-rouge » ToDo

Cette étape consiste à récupérer une première version minimaliste du code de l’application Web « ToDo » qui est notre fil-rouge.

Cette application Symfony très simple permet pour l’instant d’effectuer la consultation d’une liste de tâches (todo list). Elle est codée en PHP avec les bibliothèques de Symfony.

Elle offre pour l’instant une interface fonctionnant principalement en ligne de commande.

3.1. TODO Étape 2-a : Récupérer le code du projet Symfony

Récupérer une première version de Todo, prête à fonctionner en ligne de commande

Exécutez les commandes suivantes :

cd $HOME/CSC4101/tp-01/
symfony composer create-project oberger/tspcsc4101-todo-skeleton todo-app "v2.*" -n

Comme vous le voyez, Composer télécharge une version spécifique (2.x) de l’application ToDo à partir du projet « oberger/tspcsc4101-todo-skeleton », qui a été préparé pour les besoins du cours.

Le code de ce projet Symfony est extrait dans le répertoire ~/CSC4101/tp-01/todo-app/.

Explications de la signification des différents éléments de cette invocation :

composer
invocation de Composer, l’outil de gestion de paquetages PHP
create-project
commande create-project de composer qui crée un projet à partir d’un squelette existant (cf. documentation composer)
oberger/tspcsc4101-todo-skeleton
référence du squelette d’application qui a été préparé pour le cours
todo-app
répertoire du projet à créer
v2.*
version particuilère à télécharger : 2.x (la dernière version de la branche 2 ayant été publiée via packagist.org)
-n
option --no-interaction de composer create-project permettant de répondre avec les valeurs par défaut en cas de confirmations demandées pour l’exécution de recettes flex (configuration et génération de fichiers utiles)

3.2. TODO Étape 2-b : Observation du contenu du projet créé

Vérifiez le contenu du répertoire todo du projet Symfony qui vient d’être créé.

ls todo-app/

Pour l’instant, repérez les éléments principaux qui vont nous intéresser :

bin  composer.json  composer.lock  config  public  src  symfony.lock  todo.sqlite  var  vendor
composer.json

descriptif du projet Composer de l’application. Il référence notamment les bibliothèques PHP utilisées dans ce projet basé sur Symfony;

Vous pouvez consulter son contenu (format JSON) dans un éditeur de texte.

Au fur et à mesure de la vie d’un projet Symfony on pourra utiliser Composer à nouveau, pour y ajouter des dépendances, les bibliothèques de développement dont on aura besoin au cours de l’ajout des fonctionnalités.

bin/console
script exécutable PHP présent dans le sous-répertoire bin/, utilisé en ligne de commande pour le développement Symfony, chaque fois qu’on va invoquer symfony console en ligne de commande;
src/
cœur du squelette d’application Symfony. Il contient le code source PHP de l’application, sur lequel vous travaillerez principalement par la suite;
templates/
sources des gabarits Twig de l’application, sur lesquels vous serez amenés à travailler également, quand vous concevrez les pages Web;
vendor/

contient le code de toutes les bibliothèques PHP du framework Symfony qui seront utilisées. Elles ont été téléchargées par Composer, et y seront mises à jour en cas d’évolutions (correction de bugs, etc.).

Interdiction de modifier manuellement le contenu de ce répertoire, sous peine de malédiction. On ne touche qu’au code qui est dans src/.

3.3. TODO Étape 2-c : Prise en main de l’outil console en ligne de commande de Symfony

Cette séquence a pour but d’examiner les fonctionnalités utilisables en ligne de commande par les développeurs, qui sont fournies par le composant console de Symfony

Le script bin/console fourni dans le projet permet d’accéder en ligne de commande (dans un terminal) à différentes fonctionnalités du cadriciel Symfony, notamment des utilitaires intéressants pour les phases de développement et de tests.

On lancera les commandes via l’invocation symfony console de l’outil Symfony en ligne de commande préalablement installé.

Cette façon de l’invoquer, mentionnée dans nos supports, devrait rendre plus portable les instructions pour les différents systèmes d’exploitation.

Au cas où la commande symfony console ne fonctionnerait pas, vous pouvez aussi lui substituer l’invocation directe du script PHP bin/console (ou un équivalent, comme php bin/console).

Essayez les commandes suivantes dans un terminal, depuis l’intérieur du répertoire du projet Symfony :

  1. Vérifiez la version de Symfony utilisée dans le projet

    cd $HOME/CSC4101/tp-01/todo-app/
    symfony console -V
    
    Symfony 6.4.10 (env: dev, debug: true) [...]
    

    Vous êtes bien en environnement de développement dev (pas encore prêts pour la production !)

    Si vous voyez l’affichage « Symfony CLI version 5.10.2 (c) 2021-2024 Fabien Potencier (2024-07-19T11:09:07Z - stable) », c’est que vous n’êtes pas dans le bon répertoire; votre shell n’est pas positionné à l’intérieur du projet.

  2. Affichez la liste des sous-commandes disponibles (avec la sous-commande list, ou directement sans argument passé à symfony console) :

    symfony console list
    
  3. Consultez l’aide en ligne avec la sous-commande help. Par exemple :

    symfony console help about
    
  4. Et consultez donc également la sortie fournie par cette sous-commande about :

    symfony console about
    

    Cette commande donne différentes informations sur l’environnement de développement et de mise au point.

Bravo, vous maîtrisez les commandes de base, et on peut passer à la découverte de l’application.

4. Étape 3 : Première exécution de l’application, en ligne de commande

Cette étape consiste à exécuter l’application en ligne de commande dans l’environnement de développement, sur une base de données de tests locale

Maintenant que l’outil Console de Symfony fonctionne en ligne de commande, il est temps de tester le code de l’application Todo prévu pour la ligne de commande.

On va tester le lancement de la première commande de requêtage de la liste des tâches contenues dans la base de données : app:list-todos.

4.1. TODO Étape 3-a : Génération de la base de données de développement dans SQLite

Cette étape a pour but de créer une base de données de tests avec les outils intégrés de Doctrine.

On va re-créer la base de données de l’application, qui nous sert à effectuer des tests en local pendant le développement et la mise au point du code.

Durant le développement de nos applications, le modèle de données évoluera progressivement. Vous serez amenés à répéter régulièrement les opérations que nous décrivons ici. Souvenez-vous-en, ou notez-les dans un fichier de notes qui vous suivra pendant les différentes séances de TP et/ou le projet.

Effectuez les opérations suivantes dans un terminal en ligne de commande :

  1. Supprimez le fichier de base de données SQLite existant :

    symfony console doctrine:database:drop --force
    
  2. Créez un fichier de base de données SQLite (vide), en exécutant la commande suivante :

    symfony console doctrine:database:create
    ls -l *.sqlite
    

    En fait, il se trouve que cette commande se contente de créer un fichier vide, ce qui correspond au comportement nécessaire pour une base de données légère comme SQLite. Mais dans le cas où on utiliserait un autre SGBD, ceci peut s’avérer nécessaire.

  3. Créez le schéma de la base SQLite (tables, index, etc.), en utilisant la sous-commande doctrine:schema:create :
    1. Commencez-donc par vérifier ce qui serait fait :

      symfony console doctrine:schema:create --dump-sql
      
      CREATE TABLE todo (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
      title VARCHAR(255) DEFAULT NULL,
      completed BOOLEAN NOT NULL,
      created DATETIME DEFAULT NULL,
      updated DATETIME DEFAULT NULL);
      

      Le code SQL affiché devrait vous sembler assez clair, pour l’instant.

    2. Puis créez le schéma pour de bon, avec :

      symfony console doctrine:schema:create
      

Bravo, vous êtes prêts à tester des requêtes dans cette base de données.

4.2. TODO Étape 3-b : Première requête de liste de tâches

Dans le squelette fourni, les développeurs de ToDo on codé une interface en ligne de commande.

  1. Appelez la première commande de l’application list-todos :

    symfony console app:list-todos
    
  2. Sans surprise, cette commande affiche la liste des tâches connues dans la base de données de l’application : [ERROR] no todos found!.

    Ça fonctionne nominalement… mais la base de données est vide, vu qu’on vient de la recréer dans l’étape précédente.

Heureusement pour nous, les développeurs ont aussi préparé un jeu de données de test, qu’on peut utiliser.

4.3. TODO Étape 3-c : Chargement des données de tests

Chargez enfin les données de tests qui ont été préparées dans le squelette fourni :

  1. Utilisez la commande Symfony du module Doctrine pour charger des données de tests (fixtures) :

    symfony console doctrine:fixtures:load -n
    
       > purging database
       > loading App\DataFixtures\AppFixtures
    
    

    la base de données doit maintenant contenir ces données de tests.

  2. Vérifiez en relançant la commande app:list-todos

Cette fois, l’exécution de app:list-todos fonctionne : il y a bien des données dans la base, et on y accède en lecture !

4.4. TODO Étape 3-d : Autres fonctions de la ligne de commande de l’application ToDo

  1. Vous avez déjà découvert la première fonction de l’application, disponible en ligne de commande pour les développeurs :

    cd $HOME/CSC4101/tp-01/todo-app/
    symfony console app:list-todos
    

    Sans surprise, cette commande affiche la liste des tâches connues dans la base de données de l’application.

  2. Vérifiez les autres sous-commande app:* présentes :

    symfony console list app
    
  3. Utilisez maintenant la commande app:show-todo pour afficher les détails d’une tâche.

Comme vous pouvez le constater, notre application se présente, pour l’instant, de façon très rudimentaire, en ligne de commande.

Cette interface en ligne de commande permet juste de vérifier le bon fonctionnement du code gérant le modèle de données, avec le requêtage en lecture dans la base de données. Rien de très sorcier, mais c’est mieux quand ça marche.

4.5. TODO Rappel des commandes utiles pour plus tard

Certaines commandes que vous venez d’utiliser vont vous servir très souvent pendant le développement, donc autant les noter pour s’y référer par la suite :

  • symfony console doctrine:database:drop --force (suppression de la base de données)
  • symfony console doctrine:database:create (création de la base de données vide)
  • symfony console doctrine:schema:create (création du schéma dans la base de données)
  • symfony console doctrine:fixtures:load -n (chargement des donnéees de tests fixtures)

5. Étape 4 : Prise de connaissance du code de l’application ToDo

Cette étape consiste à découvrir le code de l’application, pour approfondir la présentation rapide effectuée en cours magistral.

Il est maintenant temps d’examiner le code pour comprendre comment fonctionne cette commande.

Mais pour ce faire, on doit d’abord s’assurer de configurer correctement l’IDE permettant de naviguer dans le code PHP de l’application Symfony.

5.1. Étape 4-a : Configuration initiale de l’IDE pour ce nouveau projet

Pour naviguer dans le code, on va utiliser un IDE (Integrated Development Environment) compatible avec PHP et Symfony

5.1.1. Recommendation sur le choix de l’IDE à utiliser

Votre IDE doit supporter PHP et les spécificités du code pour Symfony dans un projet Composer.

Nous vous indiquons les étapes correspondantes pour l’IDE Eclipse ci-dessous (que vous avez installé dans la séquence de travail hors-présentiel précédente).

Pour d’autres IDE, la procédure est différente, mais l’essentiel est de pouvoir éditer le code PHP, et d’avoir le support de Composer opérationnel dans l’IDE.

Attention, le support du langage PHP objet dans l’IDE doit inclure les attributs PHP 8.

Une version récente est donc nécessaire, comme par exemple Eclipse à partir de la version 2023.06.

5.1.1.1. Conseil pour l’organisation du Workspace Eclipse

Un Workspace Eclipse (espace de travail) permet de sauvegarder, entre deux séances de travail, la configration de l’IDE, avec la liste des projets en cours, etc.

Attention à ne pas utiliser comme répertoire de stockage de ce Workspace, un répertoire interne à un projet Symfony.

On poura par exemple conserver un Workspace unique dans $HOME/CSC4101/workspace/, qui référencera les différents projets PHP qu’on importera dans l’espace de travail.

Au fur et à mesure qu’on travaillera dans d’autres sous-répertoires de $HOME/CSC4101/, on obtiendra la structure de répertoire suivante :

  • $HOME/CSC4101/
    • workspace/
    • demo-symfony/
    • tp-01/
      • tp-01/todo-app/

Les imports des différents projets Composer/Symfony présents dans les sous-répertoires les fera apparaître dans une seule hiérarchie dans l’espace de travail, vu de l’interface d’Eclipse :

  • demo-symfony
  • todo-tp-1

Cette configuration sera sauvegardée dans ce seul Workspace stocké dans $HOME/CSC4101/workspace/, de façon séparée de l’emplacement des code source des projets.

On pourra garder différents projets ouverts simultanément dans Eclipse (pour faire du copier/coller par exemple), ou les fermer (dans le workspace) sans pour autant les supprimer du disque.

5.1.2. TODO Chargement des sources de ToDo dans Eclipse

Effectuez les opérations suivantes :

  1. Démarrez Eclipse
  2. Dans le dialogue « Select a directory as workspace », choisissez de charger un autre Workspace que celui par défaut (Browse), et créez un nouveau répertoire dans $HOME/CSC4101/ appelé « workspace », par exemple.

    Une fois sélectionné, le chemin du champ Workspace pointe sur ce nouveau chemin /..../CSC4101/workspace.

  3. Cliquez sur Launch. Eclipse se lance. Fermez la sous-fenêtre d’accueil « Welcome »
  4. Dans le Project explorer, sélectionnez « Import… » et dépliez le contenu de l’arbre sous « PHP ».
  5. Choisissez « Existing Composer Project », puis Next
  6. Cliquez sur Browse en face de Source path, et sélectionnez le répertoire correspondant à votre $HOME/CSC4101/tp-01/todo-app dans le sélecteur de fichiers. Validez.
  7. Le dialogue vous demande de saisir un identifiant de projet pour Eclipse (« Please enter a project name. »). Saisissez-le (par exemple « todo-tp-1 »), et cliquez sur Finish.

Le projet se charge et Eclipse indexe le contenu des sources PHP (DLTK indexing in progress…).

Maintenant que le projet a été configuré dans votre workspace Eclipse, vous pourrez l’y retrouver à chaque lancement d’Eclipse, sans avoir besoin de refaire ces étapes d’importation.

5.2. TODO Étape 4-b : Configuration du projet Symfony pour pouvoir tester l’application en contexte de développement

Il est maintenant nécessaire de vérifier la configuration du projet Symfony pour pouvoir tester le code de l’application en environnement de développement.

Symfony gère la notion d’environnements ayant différentes configurations au sein d’un même projet : développement, tests, production.

Nous sommes actuellement en phase de développement, en local sur notre machine, avec déjà pas mal d’outils pour tester notre code fournis par le cadriciel Symfony.

Notez l’existence du fichier « .env » à la racine du projet Symfony :

ls -a
cat .env

Ce fichier .env définit les valeurs de certaines variables de configuration du projet Symfony, dans le répertoire courant. Ces valeurs définissent les spécificités de l’« environnement Symfony » dev (utilisé en phase de développement).

Ces valeurs diffèrent des valeurs cibles pour le déploiement futur de l’application une fois qu’elle sera mise en production (cf. les valeurs par défaut du fichier « .env.dist »).

5.3. TODO Étape 4-c : Vérification de la configuration du SGBD à utiliser pour les tests

Les données affichées par l’application proviennent d’une base de données relationnelle

Dans l’environnement de développement et de mise au point (dev), vous utiliserez le SGBD SQLite qui sera suffisant pour nos besoins.

SQLite stocke la base de données relationnelle dans un fichier présent dans le répertoire du projet Symfony.

Vérifiez le contenu du fichier (caché) .env présent à la racine du projet, qui définit des variables d’environnement, comme les paramètres d’accès à la base de données :

  1. Chargez le le fichier .env dans l’IDE

    Attention, c’est un fichier caché… il faut éventuellement le faire apparaître dans l’IDE.

    Pour cela, dans Eclipse :

    • cliquez sur le bouton avec trois petits points verticaux, en haut à droite de la colonne du « Project Explorer »;
    • puis sur « Filters and Customizations »;
    • dans la liste des « Pre-Set filters », dé-cochez « .* ressources », puis « OK ».

    Les fichiers cachés apparaissent dans le projet, dont le fichier .env.

  2. Ouvrez .env, et vérifiez la valeur de la variable DATABASE_URL, présente dans la section doctrine/doctrine-bundle.

    Elle doit typiquement prendre la valeur sqlite:///%kernel.project_dir%/todo.sqlite, afin d’utiliser SQLite pour les besoins du développeur qui teste en local :

    DATABASE_URL=sqlite:///%kernel.project_dir%/todo.sqlite
    

    Ainsi, la base de données SQLite utilisée pour les tests sera créée dans $HOME/CSC4101/tp-01/todo-app/todo.sqlite.

5.4. TODO Étape 4-d : Examen du contenu de la base de données

La base de données avait été peuplée pour la première exécution de l’application qu’on a faite plus haut.

Examinez son contenu via les opérations suivantes :

  1. Vérifiez que la base de données est présente

    ls -l todo.sqlite
    sqlite3 todo.sqlite ".tables"
    

    Elle doit contenir la table : todo

    Vous pouvez aussi utiliser l’outil GUI sqlitebrowser pour consulter le contenu de la base de données, s’il est installé : sqlitebrowser todo.sqlite.

  2. Consultez les détails du contenu de la table todo :

    symfony console dbal:run-sql "select * from todo"
    

    ou bien directement :

    sqlite3 todo.sqlite "select * from todo"
    
  3. Affichez le schéma de cette table :

    sqlite3 todo.sqlite ".schema todo"
    

Cette base de données semble bien correspondre au modèle de données de notre application au vu de ce qu’on en a compris jusqu’ici.

C’est logique, puisqu’elle a été créée via l’outil Doctrine inclus dans l’environnement de développement de Symfony lors de l’appel à symfony console doctrine:schema:create.

Vérifions maintenant où est spécifié ce modèle de données, dans le code des classes PHP de notre application.

6. Étape 5 : Examen du code PHP Symfony

Cette étape va permettre d’étudier le code PHP du modèle de données de notre application Symfony, permettant de consulter des tâches.

Cette étape a pour but de rafraîchir les connaissances en objet, et bases de données.

Dans cette étape, vous allez consulter du code objet d’un projet Symfony, qui repose sur de nombreux concepts potentiellement complexes à comprendre en détail pour l’instant.
Essayez de suivre les indications données, pour vous repérer sommairement dans ce code, sans nécessairement essayer de décoder tous les détails. Nous les reverrons par la suite dans les prochaines séquences du cours.

6.1. Principes de gestion du modèle de données avec l’ORM Doctrine

On souhaite gérer des tâches (« to dos ») dans notre application.

Dans notre application Symfony, on va gérer ces éléments aussi bien :

  • en mémoire durant l’exécution du programme PHP;
  • dans la base de données relationnelle, qui stockera les données entre deux exécutions de notre programme,

On va gérer les structures de données correspondantes avec deux approches conjointes :

  • le modèle objet, pour la représentation en mémoire et les traitements à l’exécution;
  • le modèle relationnel, pour le stockage (persistence) dans la base de données.

En base de données, on aura donc la relation todo, stockée sous forme de table, accessible avec SQL, qu’on a vue plus haut.

Du côté objets, on utilisera la bibliothèque PHP Doctrine, qui est un ORM (Object Relational Mapper). Son rôle est de permettre la gestion de collections d’objets, d’associations entre entités, et de fournir le chargement/sauvegarde des objets depuis la base de données.

Pour faire court, le principe de l’ORM est de convertir chaque instance d’une relation (chaque ligne d’une table de la base de données) en instance d’une classe PHP en mémoire, et inversement. Doctrine appelle Entité ces classes d’objets.

Le programmeur peut ainsi écrire des classes PHP utilisant les fonctionnalités de Doctrine, et ainsi gérer des instances des entités, ou des collections d’entités. Quand deux entités sont liées (association 1-n, par exemple), des fonctions permettent de gérer cette association « OneToMany » et d’accéder à la collection des entités liées facilement.

6.2. TODO Étape 5-a : Examen du code exécuté à l’appel de la commande app:list-todos

Le code PHP qui exécute le traitement de la commande app:list-todos se trouve dans le fichier src/Command/ListTodosCommand.php du projet.

  1. Consultez le fichier src/Command/ListTodosCommand.php dans l’IDE
  2. Consultez la déclaration de la classe avec ses attributs :

    #[AsCommand(
        name: 'app:list-todos',
        description: 'List the todos',
        )]
    class ListTodosCommand extends Command
    {
    

    Les attributs PHP 8 définissent des méta-données utiles au framework Symfony. Ici, AsCommand permet de décrire la commande, en définissant son nom, et la description qui sera affichée dans la liste des commandes (symfony console list app).

    On a donc une nouvelle commande appelable depuis la console via symfony console app:list-todos.

  1. Consultez maintenant le cœur du sujet, dans le code de la méthode execute(). Comprenez-vous le rôle de cette méthode ?

    $io = new SymfonyStyle($input, $output);
    
    // fetches all instances of class Todo from the DB
    $todos = $this->todoRepository->findAll();
    //dump($todos);
    
    if(!empty($todos)) {
        $io->title('list of todos:');
        $io->listing($todos);
    } else {
        $io->error('no todos found!');
        return Command::FAILURE;
    }
    
    return Command::SUCCESS;
    

    Quelques commentaires :

    • l’utilitaire SymfonyStyle fourni par Symfony permet de mettre en forme des affichages sur la sortie standard dans la console :
      title()
      affiche un message de titre (colorisé)
      listing()
      affiche une liste d’éléments sous forme de liste à puces
      error()
      affiche un message d’erreur (colorisé)
    • $todoRepository est une propriété de notre classe ListTodosCommand, qui est un Repository d’objets (TodoRepository) pour accéder à la base de données qui stocke les instances de tâches.

      On peut charger une collection de toutes les instances de tâches de la base de données, avec l’appel à findAll() sur ce Repository. Elle renvoie une « liste/tableau » d’instances de la classe Todo, une pour chaque ligne de la table todo de la base de données.

      Remarque : il faudrait aller voir le code de TodoRepository (présent dans src/Repository/TodoRepository.php) pour voir comment cette classe est liée à la classe Todo, qui n’apparaît pas ici par magie.

      Mais ce serait aller explorer des éléments un peu trop complexes pour le moment. On y reviendra dans les séquences du cours ultérieures.

      Pour l’instant, on peut se contenter de voir l’aide fournie par l’IDE qui montre le prototype de la méthode findAll() si on laisse le pointeur fixe sur son appel dans l’instruction $todos = $this->todoRepository->findAll(); :

      App\Repository\TodoRepository::findAll() : Todo[] 
      
      Returns:
       Todo[]
      

      Même si on ne connaît pas tous les détails du rôle d’un repository on sait désormais que cette méthode renvoie un tableau d’instances de Todo. De l’utilité d’avoir un IDE comprenant les projets Composer, le PHP objet, etc.

    Ce code suffit donc à afficher toutes les tâches présentes dans la base de données, mises en forme de façon agréable sur la sortie standard en ligne de commande.

    Regardons à présent à quoi ressemblent ces « tâches » dans le code, avec cette fameuse classe Todo.

6.3. TODO Étape 5-b : Examen du code de l’entité Todo du modèle de données

Les objets manipulés par notre application constituent le modèle de données. Ici, un ensemble de tâches codées grâce à la classe PHP Todo.

Dans un projet Symfony utilisant Doctrine, les classes du modèle de données sont définies dans les fichiers source présents dans src/Entity/ (complétés par les classes de src/Repository/).

Voyons ce que les développeurs de Todo y ont codé en PHP objet :

  1. Consultez src/Entity/Todo.php dans l’IDE, pour consulter le code de la classe Todo.

    Notez la convention de structuration du projet utilisée, pour faciliter la correspondance entre les noms des classes, avec les espaces de noms (namespaces) PHP, et leur emplacement dans la structure de répertoires du code source :

    • Namespace : App\Entity
    • Classe : Todo (convention : toute classe commence par une capitale)
    • Répertoire : src/Entity
    • Fichier source : Todo.php (une seule classe par fichier source PHP)

      L’auto-loader PHP utilisé dans les projets Symfony basés sur Composer, parcourt automatiquement les répertoires de sources du projet (principalement src/ et de ses bibliothèques, en dessous de vendor/) pour charger les fichiers sources, et détecter les déclarations de namespace permettant de résoudre ultérieurement les imports via l’instruction use.

      Exemple :

      namespace App\Entity;
      
      ...
      
      class Todo {
      
          ...
      
      use App\Entity\Todo;
      
      ...
      
          $this->todoRepository = $doctrineManager->getRepository(Todo::class);
      
      ...
      
      
  2. Pouvez-vous identifier les propriétés (variables d’instances) définies dans la classe Todo ?

    Vous remarquez que chacune de ces propriétés est décorée avec un attribut ORM\Column qui permet à Doctrine de savoir comment en gérer la persistence.

    On retrouve la correspondance avec les éléments renvoyés par sqlite3 todo.sqlite ".schema todo" ou symfony console doctrine:schema:create --dump-sql :

    propriété de classe type PHP attribut Doctrine colonne en base
    Todo::id int ORM\Id, ORM\GeneratedValue INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
    Todo::title string ORM\Column(length: 255, nullable: true) VARCHAR(255) DEFAULT NULL
    Todo::completed bool ORM\Column BOOLEAN NOT NULL
    Todo::created Datetime ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true) DATETIME DEFAULT NULL
    Todo::updated Datetime ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true) DATETIME DEFAULT NULL

    Vous savez donc maintenant où et comment on peut programmer ces attributs ORM destinés à Doctrine pour la persistence en base de données, qui définissent les conversions de type, les contraintes de stockage, de valeurs non-nulles, etc. On y reviendra en détails ultérieurement.

  3. Observez la méthode __toString() qui réalise la conversion en chaîne de caractères (pour l’affichage sur la console, entre autres).

6.4. TODO Étape 5-c : Examen du code de chargement des données de tests (Fixtures)

Examinons un exemple de code de mise à jour du contenu de la base de données, celui qui s’exécute lors de l’invocation de symfony console doctrine:fixtures:load pour charger les données de tests dans la base locale.

Examinez le code dans src/DataFixtures/AppFixtures.php

Le code de la méthode AppFixtures::loadTodos() va appeler autant que nécessaire getTodosData(), pour récupérer des couples (titre, complété), et les sauvegarder comme autant de tâches, dans la base de données.

On fonctionne en deux temps :

  1. création d’une instance de tâche en mémoire, pour chaque couple, en utilisant des instructions standard en PHP objets (appel au constructeur, puis aux setters):

    $todo = new Todo();
    $todo->setTitle($title);
    $todo->setCompleted($completed);
    
  2. sauvegarde des données via les appels à persist() et flush() de Doctrine, qui vont générer la requête (INSERT) en base de données.

    Inutile d’en savoir beaucoup plus pour l’instant.

Voilà pour l’examen du code basique permettant la gestion des données en mémoire et leur chargement / sauvegarde dans la base de données, en utilisant Doctrine.

6.5. Étape 5-d : Examen des requêtes SQL (optionnel)

Exécutez la commande :

symfony console list app

Vous voyez que des commandes sont disponibles pour modifier le contenu de la base de données.

Vous pouvez jouer avec l’ajout ou la suppression et voir si ça a l’air de fonctionner.

6.6. TODO Étape 5-e : Examen des logs d’exécution

Au fur et à mesure de l’exécution d’une application Symfony, des traces (logs) sont sauvegardées dans un fichier.

C’est le cas en particulier en environnement de développement, pour mieux identifier les problèmes à l’exécution.

Lancez la commande suivante :

cat var/log/dev.log

Vous voyez beaucoup de choses, car certains composants de Symfony affichent des infos très verbeuses.

Pour surveiller l’affichage de ces traces en temps réel, on peut ouvrir un autre terminal en parallèle et exécuter par exemple :

tail -f var/log/dev.log

Vous pouvez vérifier s’il y a des messages importants, par exemple avec :

grep -a --color=auto "ERROR\|CRITICAL" var/log/dev.log

Il se peut qu’il n’y ait rien d’affiché, ce qui serait bon signe. Essayez alors de taper :

symfony console app:null
grep -a --color=auto "ERROR\|CRITICAL" var/log/dev.log

Vous devriez maintenant voir votre erreur, tracée dans ce journal.

6.7. TODO Étape 5-f : Examen des requêtes SQL

Essayez de lancer à nouveau l’exécution des différentes commandes vues dans cette séquence, aussi bien en lecture qu’en modifications (cf. étape précédente).

Immédiatement après l’exécution de chaque commande, vérifiez le contenu du fichier de traces (logs) var/log/dev.log.

Vous pouvez par exemple exécuter :

symfony console app:list-todos
grep "doctrine.DEBUG" var/log/dev.log

La commande grep va filtrer le contenu du fichier de traces pour n’afficher que ce qui est relatif à Doctrine.

Vous devriez voir passer toutes les requêtes SQL effectuées par les commandes. Pratique pour la mise au point en cas de problèmes, si le code ne fait pas ce qu’on veut.

Nous reviendrons plus tard dans le cours sur la façon dont les requêtes SQL sont générées par Symfony avec Doctrine.

7. Étape 6 : Exécution d’une application Symfony en mode Web

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.

Quittons maintenant l’environnement en ligne de commande, pour tester rapidement le fonctionnement d’une application Symfony sur le Web.

L’application ToDo fournie dispose en effet également d’une interface Web pour HTTP. Rien que de très normal, en fait pour une application d’un cadriciel Web comme Symfony.

Découvrons comment tester, en local, une application Web développée avec Symfony.

7.1. TODO Étape 6-a : Lancement du serveur HTTP intégré à Symfony sur l’application ToDo

Démarrer le serveur Web local intégré à l’interpréteur PHP pour pouvoir tester les interactions HTTP en local.

Depuis la ligne de commande, vous aller lancez le serveur HTTP intégré à l’outil Symfony en ligne de commande.

Le serveur rend accessible via HTTP des ressources disponibles dans le répertoire du projet Symfony d’où il a été lancé. Quand ces ressources correspondent à des programmes PHP, il les exécute pour faire interpréter les requêtes HTTP par ces programmes.

Les instructions suivantes sont données pour un environnement en ligne de commande native (GNU/Linux). Adaptez à votre environnement en cas d’autre système, notamment pour les numéros de port à utiliser, par exemple.

Lancez la commande suivante :

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

Le serveur se lance et affiche quelque chose du genre :


 [OK] Web server listening                                                                                              
      The Web server is using PHP CLI 8.2.7                                                                             
      http://127.0.0.1:8000                                                                                             
[Web Server ] Jul 10 13:15:56 |DEBUG  | PHP    Reloading PHP versions 
[Web Server ] Jul 10 13:15:56 |DEBUG  | PHP    Using PHP version 8.2.7 (from default version in $PATH) 
[Web Server ] Jul 10 13:15:56 |INFO   | PHP    listening path="/usr/bin/php8.2" php="8.2.7" port=38555
[PHP        ] [Mon Jul 10 13:15:56 2023] PHP 8.2.7 Development Server (http://127.0.0.1:38555) started

Vous pouvez en principe ignorer les warnings, pourvu que le message « [OK] » soit bien affiché.

7.2. TODO Étape 6-b : Accès à l’interface Web avec le navigateur

  1. Chargez l’URL http://localhost:8000/ (ou http://127.0.0.1:8000) dans un navigateur Web (lancé sur la même machine)

    Vous constatez le comportement suivant :

    • Une page Web affiche « Welcome to Symfony 6.4.x »
    • dans le terminal, le serveur Web affiche quelque chose du style :

      [PHP        ] [Fri Aug 26 15:55:05 2024] ::1:33926 [404]: GET /
      

    Le serveur fonctionne, et affiche les traces (logs) de fonctionnement au fur et à mesure des connexions du navigateur.

  2. Remarquez la structure des traces : cette ligne affiche différentes informations :

    [PHP        ] [Fri Aug 26 15:55:05 2024] ::1:33926 [404]: GET /
    
    Fri Aug 26 15:55:05 2024
    date de réception de la requête HTTP lors du chargement de l’URL à partir du navigateur
    ::1:33926
    source de la requête : ici, client HTTP (le navigateur Web) connecté depuis localhost (::1 en IPv6, émettant sa requête depuis le port source 33926 (ou un équivalent IPv4 du genre : 127.0.0.1:44530)
    (no term)
    404 : code de réponse HTTP (404 Not Found), renvoyé par le serveur
    GET
    méthode HTTP demandée dans la requête du client
    /
    chemin d’accès à la ressource demandée : le client a accédé à http://localhost:8000/, donc à la ressource racine du serveur Web : « / » .

    Pour l’instant, on n’a pas étudié le protocole HTTP, mais vous devriez probablement avoir ententu parler de cette infame erreur 404.

    On constate donc que le serveur Web répond avec un code d’erreur HTTP (404), ce qui signifie qu’il n’a pas trouvé ce qui est demandé…

  3. Comment expliquer que ce code de réponse 404 est renvoyé, alors que le navigateur affiche bien une page ?

    Le serveur Web a bien effectué un traitement, en réponse à la requête du navigateur de chargement de la ressource /.

    Il a en fait démarré l’exécution de notre application PHP Symfony, en contexte Web, cette fois-ci.

    Le serveur nous dit : « 404 : il n’y a rien à la racine du site ! »

    En effet, du fait de ce qui est présent dans le code de l’application, Symfony ne trouve pas de resource à afficher, mais nous renvoie néanmoins une page HTML, pour habiller le message d’erreur : un code de réponse 404 (Not found).

    Par exception, comme nous sommes en environnement de développement, Symfony nous accueille avec un tel message informatif en cas d’erreurs. Notez qu’on voit ce code « 404 » apparaître en rouge en bas à gauche de la page Web.

  4. Essayez sur http://localhost:8000/todo/list : est-ce mieux, maintenant ?

    OK, dans cette version très basique de l’application Todo, les développeurs ont mieux géré la présentation en ligne de commande que le look des pages Web… normal, ça viendra en temps utile dans la suite du cours.

Notez que l’application PHP s’exécute en « boucle », réagissant en réponse aux requêtes transmises à travers le serveur HTTP.

Si vous mettez à jour le code, ces mises à jour seront prises en compte sans avoir à relancer le serveur HTTP.

Vous pouvez ainsi garder le terminal ouvert avec le serveur lancé dedans, pour pouvoir consulter les logs sur la sortie dans le terminal au fur et à mesure de la mise au point.

8. Étape 7 : Découverte du tableau de bord d’administration EasyAdmin

Cette étape introduit le module EasyAdmin qui permet d’ajouter, avec un minimum de code, un tableau de bord d’administration dans une application Symfony.

En plus de la page Web un peu rustique qu’on vient de consulter, qui a été codée par les développeurs, notre application Todo vous est fournie avec un tableau de bord d’administration que les développeurs ont réalisé très simplement grâce à l’outil EasyAdmin pour Symfony, à titre de démonstration.

Le tableau de bord d’administration offre une interface Web prête à l’emploi permettant de consulter et modifier le contenu de la base de données.

8.1. TODO Étape 7-a : Prise en main de Todo avec son EasyAdmin ?

Tester l’ajout, la modification et la suppression de données dans l’application ToDo.

Connectez-vous sur la partie /admin de l’application avec votre navigateur. Par exemple sur http://localhost:8000/admin.

Testez que vous pouvez visualiser le contenu de la base de données de l’application ToDo, et modifier le contenu de la base.

A priori, cette interface Web est beaucoup plus conviviale que l’interface en ligne de commande offerte par les lancements de symfony console app:update-todo et consors.

Elle a aussi l’immense avantage d’être utilisable via une interface Web. Pas besoin de devoir être connecté dans un terminal sur le serveur Web pour pouvoir modifier le contenu de la base de données.

Est-ce qu’un tel système est suffisant pour réaliser des applications Web, sans presque écrire de code ?

Il y a des chances que les fonctionnalités ne suffisent pas (en gros on a une gestion minimale de base de donnée avec une surcouche Web).

En plus, donner les droits à tout le monde de tout modifier, ce n’est sûrement pas acceptable en production !

Même si on peut déployer en production un tableau de bord d’administration réalisé avec EasyAdmin, on veillera à le faire sur une section du site protégée par du contrôle d’accès.

On verra plus tard dans le cours comment protéger les accès aux pages.

Une fois que vous avez terminé, vous pouvez interrompre l’exécution du serveur Web de tests en tapant Ctrl+C dans le terminal.

9. Évaluation

À l’issue de cette séance vous avez travaillé sur les éléments suivants :

  • vous avez initié un projet Symfony à partir d’un squelette fourni
  • vous comprenez certaines fonctions de l’outil composer pour le téléchargement de bibliothèques, ou de squelettes de code
  • vous connaissez l’outil symfony console qui est utilisé par le développeur Symfony en ligne de commande
  • vous avez découvert le modèle de données objet codé en PHP permettant de gérer en mémoire des objets (classes objet décorées avec des annotations Doctrine)
  • vous savez recréer la base de données locale de tests
  • vous savez initialiser la base de données avec des données de test (DataFixtures)
  • vous savez où se trouvent les traces (logs) d’exécution de l’application
  • vous savez démarrer l’interface Web d’une application Symfony
  • vous savez comment modifier les données s’il y a un tableau de bord d’administration Web réalisé avec EasyAdmin

10. Pour aller plus loin (optionnel)

Nous vous proposons de façon complètement optionnelle d’aller un peu plus loin que ce qui est proposé par défaut dans la séance.

Ces opérations risquent de prendre un temps conséquent. Attention à ne pas empiéter sur les apprentissages des autres modules.

Ne vous lancez que si vous êtes conscient de maîtriser la situation.

10.1. Compréhension d’un algorithme de filtrage des tâches pas encore terminées, dans Todo

Vous pouvez examiner le fonctionnement de la commande app:list-active-todos permettant de lister les seules tâches pas encore terminées.

Consultez le code source dans src/Command/ListactiveTodosCommand.php, dont la méthode execute() propose un algorithme trivial pour filtrer les seules tâches qui ne sont pas encore terminées.

On peut se contenter d’une telle boucle testant les tâches non terminées et les ajoutant dans un tableau des tâches actives. Mais il y a d’autres façons de faire en Symfony, en faisant intervenir le composant d’ORM Doctrine.

Les commentaires proposent ainsi une autre variante que vous pouvez tester si vous le souhaitez.

10.2. Observation du contenu de vendor/

Au tout début, dans la création du projet avec Composer, on a mentionné que le sous-répertoire vendor/ du projet contient le code de toutes les bibliothèques PHP du framework Symfony.

Mais on est passé un peu vite dessus…

À quoi correspondent ces bibliothèques ? On le verra plus en détail au fil des séances.

Essayons de découvrir un peu ce dont on parle. Par exemple Doctrine, précisément, c’est où ?

Pour l’instant, on a noté par exemple que dans le code d’une commande, un simple import d’un module PHP avec use permet de faire le lien avec Doctrine :

use Doctrine\Persistence\ManagerRegistry;

Votre IDE devrait pouvoir vous dire ce qu’il en est : positionner la souris sur le terme « ManagerRegistry » dans cette ligne. Un popup apparaît avec la documentation de cette interface : « Contract covering object managers for a Doctrine persistence layer ManagerRegistry class to implement. ». Avec Eclipse, si vous appuyez alors sur F2 (focus), vous voyez apparaître le bouton « Open Declaration ».

Si vous cliquez dessus, ça ouvre le code source du fichier vendor/doctrine/persistence/src/Persistence/ManagerRegistry.php fourni par Doctrine, qui a été installé par Composer.

Vous pouvez ainsi naviguer dans le code des composants du framework Symfony.

Pour avoir une idée de l’ampleur de ce framework, voyons ce que contient vendor/, dans ce projet relativement simple :

  • liste des bibliothèques (approximation) :

    find vendor/ -maxdepth 2
    

    retourne 130 sous-répertoires, installés par Composer sur la base des déclarations de composer.json.

  • nombre de fichiers :

    find vendor/ -type f | wc -l
    
    8318
    

    tout n’est pas que du code, mais quand même;

  • nombre de fichiers source PHP (« .php ») :

    find vendor/ -type f -name "*.php" | wc -l 
    
    6906
    

    on voit qu’une grande partie est bien quand-même du code PHP.

  • espace disque occupé :

    du -sh vendor/
    
    102M	vendor/
    

    ce n’est pas énorme vu la taille d’un disque dur moderne, mais en code source, ça en fait des lignes…

  • nombre de lignes de code (approximation large)

    find vendor -type f | xargs grep -l '<\?php' | xargs cat | sed '/^\s*$/d' | wc -l
    
    1550690
    

    Ah, quand-même, 1,5 M lignes… combien de bugs au kilo ? ;-)

10.3. Re-construction de l’application en partant d’un projet Symfony vide

Au début de cette séquence de TP, on a fait installer un squelette déjà prêt à l’emploi (avec composer create-project). Le code des commandes était déjà là, ayant été préparé par l’équipe enseignante.

Vous pouvez tester le fait de repartir d’un projet Symfony vide, en suivant les étapes de l’annexe ci-dessous. Attention, cela prend du temps, et n’est pas indispensable pour la suite du cours.

Non, vraiment, ça va prendre du temps, et vous avez d’autres cours à travailler, plutôt que de faire ça, n’est-ce pas !

DONE Annexes

Cette annexe documente l’essentiel des étapes utilisées pour réaliser la version 2.x de l’application Todo telle que vue dans cette séance.

Inutile de tester cela, sauf si vous avez vraiment l’intention de repartir d’une feuille blanche pour un projet personnel.

Cette procédure est fournie sans garantie d’exhaustivité.

DONE Création d’un projet Symfony grâce à Composer

Vous allez utiliser la commande new de l’outil Symfony en ligne de commande (Symfony CLI), qui va créer le répertoire de développement d’une application Symfony et y télécharger le code source d’un squelette d’application.

Cette commande s’appuie sur le gestionnaire de paquetages PHP Composer, en appelant la commande create-project de composer.

Nous avons choisi de privilégier la version LTS de Symfony pour les TP afin de stabiliser les contenus sur plusieurs années. Néanmoins vous devriez pouvoir exécuter à peu près les mêmes opérations sur la version stable de Symfony. Modifiez la version en choisissant plutôt --version=stable ci-dessous, si vous préférez tenter l’aventure.

Téléchargez et installez le squelette d’application standard de Symfony, sur lequel vous allez travailler :

cd $HOME/CSC4101/tp-01/
symfony --debug new --no-git mytodo --version=lts

Cette commande affiche plusieurs infos intéressantes pour comprendre ce qui se passe :

* Creating a new Symfony 6.4 project with Composer

...

[OK] Your project is now ready in /.../mytodo                                   

Composer a téléchargé dans un nouveau sous-répertoire mytodo/ le squelette d’un projet d’application Symfony, pour une version précise de Symfony, ici la 6.4. C’est celle qui correspond à la version LTS (Long-Term Support) de Symfony (au moment où on exécute la commande symfony new --version=lts).

DONE Ajout des modules PHP utiles

Nous aurons besoin de différents modules pour permettre à l’application de fonctionner, ou pour nous aider dans le prototypage de cette application :

Doctrine
le composant d’accès à la base de données via des objets PHP
le « Maker bundle »
qui va nous permettre de générer du code PHP plutôt que d’avoir à tout coder à la main
les « DataFixtures »
qui permettent de charger automatiquement des données de test dans la base de données

Procédez à l’installation avec les commandes suivantes à l’intérieur du répertoire mytodo :

cd mytodo/
  • Monolog :

    symfony composer require symfony/monolog-bundle
    
  • ORM Doctrine :

    symfony composer require -n symfony/orm-pack
    
  • « Maker bundle » :

    symfony composer require --dev -n symfony/maker-bundle
    
  • DataFixtures :

    symfony composer require --dev doctrine/doctrine-fixtures-bundle
    

Composer a téléchargé et extrait les différentes bibliothèques PHP. On peut donc les utiliser.

DONE Ajout entité principale : « Todo »

Si vous êtes comme la plupart des programmeurs, vous aimez modérément copier-coller du code qui pourrait être généré automatiquement.

Ça tombe bien, Symfony propose différent assistants qu’on ne va pas se priver d’utiliser pour générer la base du code de nos applications, plutôt que d’écrire nous-même du code buggé.

Lancez l’assistant make:entity, dans le terminal, depuis l’intérieur du projet Symfony. Il va nous servir à générer le code de classes PHP pour gérer notre entité « Todo ».

Répondez aux questions de l’assistant pour obtenir une interaction similaire à la trace présentée ci-dessous :

symfony console make:entity
 Class name of the entity to create or update (e.g. VictoriousPuppy):
 > Todo

 created: src/Entity/Todo.php
 created: src/Repository/TodoRepository.php

 Entity generated! Now let's add some fields!
 You can always add more fields later manually or by re-running this command.

 New property name (press <return> to stop adding fields):
 > title

 Field type (enter ? to see all types) [string]:
 > 

 Field length [255]:
 > 

 Can this field be null in the database (nullable) (yes/no) [no]:
 > yes

 updated: src/Entity/Todo.php

 Add another property? Enter the property name (or press <return> to stop adding fields):
 > completed

 Field type (enter ? to see all types) [string]:
 > ?

Main Types
  * string
  * text
  * boolean
  * integer (or smallint, bigint)
  * float

Relationships/Associations
  * relation (a wizard 🧙 will help you build the relation)
  * ManyToOne
  * OneToMany
  * ManyToMany
  * OneToOne

Array/Object Types
  * array (or simple_array)
  * json
  * object
  * binary
  * blob

Date/Time Types
  * datetime (or datetime_immutable)
  * datetimetz (or datetimetz_immutable)
  * date (or date_immutable)
  * time (or time_immutable)
  * dateinterval

Other Types
  * ascii_string
  * decimal
  * guid


 Field type (enter ? to see all types) [string]:
 > boolean

 Can this field be null in the database (nullable) (yes/no) [no]:
 > 

 updated: src/Entity/Todo.php

 Add another property? Enter the property name (or press <return> to stop adding fields):
 > created

 Field type (enter ? to see all types) [string]:
 > ?

Main Types
  * string
  * text
  * boolean
  * integer (or smallint, bigint)
  * float

Relationships/Associations
  * relation (a wizard 🧙 will help you build the relation)
  * ManyToOne
  * OneToMany
  * ManyToMany
  * OneToOne

Array/Object Types
  * array (or simple_array)
  * json
  * object
  * binary
  * blob

Date/Time Types
  * datetime (or datetime_immutable)
  * datetimetz (or datetimetz_immutable)
  * date (or date_immutable)
  * time (or time_immutable)
  * dateinterval

Other Types
  * ascii_string
  * decimal
  * guid


 Field type (enter ? to see all types) [string]:
 > datetime

 Can this field be null in the database (nullable) (yes/no) [no]:
 > yes

 updated: src/Entity/Todo.php

 Add another property? Enter the property name (or press <return> to stop adding fields):
 > updated

 Field type (enter ? to see all types) [string]:
 > datetime

 Can this field be null in the database (nullable) (yes/no) [no]:
 > yes

 updated: src/Entity/Todo.php

 Add another property? Enter the property name (or press <return> to stop adding fields):
 > 



  Success! 


 Next: When you're ready, create a migration with symfony console make:migration

L’assistant a créé pour nous deux classes PHP qui utilisent Doctrine :

  • src/Entity/Todo.php : gère les instances en mémoire des tâches
  • src/Repository/TodoRepository.php : gère le chargement des tâches depuis la base de données

Rafraîchissez votre projet dans Eclipse si nécessaire pour voir apparaître ces deux classes.

Attention à lire attentivement les questions (ainsi que les messages en réponse), et à éviter le copier/coller un peu rapide.

DONE Configuration de la base de données

  1. Chargez le le fichier .env dans l’IDE

    Attention, c’est un fichier caché… il faut éventuellement le faire apparaître dans l’IDE.

    Pour cela, dans Eclipse :

    • cliquez sur le bouton avec trois petits points verticaux, en haut à droite de la colonne du « Project Explorer »;
    • puis sur « Filters and Customizations »;
    • dans la liste des « Pre-Set filters », dé-cochez « .* ressources », puis « OK ».

    Les fichiers cachés apparaissent dans le projet, dont le fichier .env.

  2. Ouvrez .env, et vérifiez la valeur de la variable DATABASE_URL.

    Elle doit typiquement prendre la valeur sqlite:///%kernel.project_dir%/var/data.db, afin d’utiliser SQLite pour les besoins du développeur qui teste en local :

    DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
    

    Ainsi, la base de données SQLite utilisée pour les tests sera créée dans $HOME/CSC4101/tp-01/todo-app/var/data.sqlite.

  3. Créez un fichier de base de données SQLite (vide), en exécutant la commande suivante :

    symfony console doctrine:database:create
    
  4. Créez le schéma de la base SQLite (tables, index, etc.), en utilisant la sous-commande doctrine:schema:create :

    symfony console doctrine:schema:create
    
  5. Ajoutez des données de test :

    symfony console dbal:run-sql "INSERT INTO todo (TITLE, COMPLETED)  VALUES ('Se passer de ChatGPT', false)"
    

DONE Ajout de l’interface console

Cette étape va permettre d’ajouter une couche de présentation dans notre application.

DONE Ajout de la commande app:list-todos

Ajoutons une interface en ligne de commande à notre application PHP, pour disposer d’une commande accessible pour le développeur via l’interface principale offerte par bin/console.

Cette fois encore, utilisons un assistant générateur de code pour nous faciliter le travail :

symfony console make:command
Choose a command name (e.g. app:victorious-popsicle):
> app:list-todos

created: src/Command/ListTodosCommand.php


 Success! 


Next: open your new command class and customize it!
Find the documentation at https://symfony.com/doc/current/console.html

Consultez le résultat généré dans src/Command/ListTodosCommand.php

Vérifiez que la commande est bien disponible via :

symfony console list app

et :

symfony console app:list-todos --help

Et enfin qu’elle répond quand on l’invoque :

symfony console app:list-todos

                                                                                                                        
 [OK] You have a new command! Now make it your own! Pass --help to see your options.                                    
                                                                                                                        

DONE Branchement de l’interface à la base de données

Pour que notre commande puisse afficher la liste des tâches on va devoir réaliser quelques branchements utiles.

Modifiez le code de src/Command/ListTodosCommand.php, pour ajouter un attribut todoRepository dans la classe ListTodosCommand, permettant de gérer l’accès à la base de données via le composant Doctrine, et son initialisation.

Copiez-collez les éléments ci-dessous pour ajouter l’attribut et le constructeur dans ListTodosCommand (attention à ne pas dupliquer la déclaration de la classe) :

// ...

use App\Entity\Todo;
use App\Repository\TodoRepository;
use Doctrine\Persistence\ManagerRegistry;

use Symfony\Component\Console\Attribute\AsCommand;
// ...


class ListTodosCommand extends Command
{
        /**
         *  @var TodoRepository data access repository
         */
        private $todoRepository;

        /**
         * Plugs the database to the command
         * 
         * @param ManagerRegistry $doctrineManager
         */
        public function __construct(ManagerRegistry $doctrineManager)
        {
                $this->todoRepository = $doctrineManager->getRepository(Todo::class);

                parent::__construct();
        }

        // ...

DONE Ajout de l’affichage de la liste des tâches

Ajoutons maintenant le code permettant de charger toutes les tâches présentes dans la base de données.

On utilise la méthode findAll() du repository Doctrine des tâches TodoRepository, qui a été généré par l’asssistant, et qui renvoie un tableau de tâches. On peut manipuler ce tableau comme un tableau PHP ordinaire, par exemple avec foreach.

Modifiez la méthode execute() de ListTodosCommand :

protected function execute(InputInterface $input, OutputInterface $output): int
{
         $io = new SymfonyStyle($input, $output);

         // récupère une liste toutes les instances de la classe Todo
         $todos = $this->todoRepository->findAll();
         //dump($todos);
         if(!empty($todos)) {
                 $io->title('list of todos:');

                 $io->listing($todos);
         } else {
                 $io->error('no todos found!');
                 return Command::FAILURE;
         }
         return Command::SUCCESS;
}

Testez avec :

symfony console app:list-todos

Le programme échoue avec une erreur (attendue) du type :

symfony console app:list-todos 

list of todos:
==============


In SymfonyStyle.php line 107:

  Object of class App\Entity\Todo could not be converted to string  


app:list-todos [--option1] [--] [<arg1>]

Ce genre de chose devrait vous paraître cohérent avec ce qui a été vu l’année passée en programmation orientée objet.

La méthode listing() de SymfonyStyle essaie d’afficher une chaîne de caractères sur la console… mais une instance de la classe Todo peut-elle être convertie en chaîne de caractères ? Pas de base, en PHP.

Réglons cela.

DONE Ajout de Todo::__toString()

Ajoutez la méthode de conversion de tâche en chaîne de caractère qui nous manque : Todo::__toString(), par exemple :

public function __toString() {
        $s = '';
        $s .= $this->getId() .' '. $this->getTitle() .' ';
        $s .= $this->isCompleted() ? '(completed)': '(not complete)';
        return $s;
}

Réessayez le lancement de notre commande…

Bravo, vous avez une application PHP objet qui effectue des requêtes en base de données et affiche le résultat sur la console. La classe !

DONE Customisation de EasyAdmin dans ToDo

Une fois que les développeurs on effectué les opérations de base pour ajouter EasyAdmin dans le code, il a été nécessaire de modifier certains paramètres pour « customiser » l’affichage des Todo.

On a également dû surcharger certains gabarits d’affichage (on reviendra sur cette notion plus tard).

Pour info, voici un exemple de TodoCrudController::configureFields() avec quelques commentaires pour expliciter le rôle des différents paramètres.

On trouvera également dans les sources du projet (templates/admin/) le code des gabarits qui n’a pas été reproduit ici.

public function configureFields(string $pageName): iterable
{
        return [
                // Id shouldn't be modified
                IdField::new('id')->hideOnForm(),
                // Completed will be rendered as a toggle only in edit
                BooleanField::new('completed')
                        ->onlyOnForms()
                        ->hideWhenCreating(),
                // otherwise it may be displayed on index as a string (as an Integer since Text won't be able to convert bool to string)
                IntegerField::new('completed')
                        ->onlyOnIndex()
                        ->formatValue(function ($value) {
                                return ($value ? 'COMPLETED' : 'ACTIVE');
                        }),
                // Title will be rendered so as to include a link, and be striked whenever completed
                TextField::new('title')
                        ->setTemplatePath('admin/fields/todo_index_title.html.twig'),
                DateField::new('created'),
                DateField::new('updated'),
        ];
}

public function configureActions(Actions $actions): Actions
{
        // For whatever reason show isn't in the menu, bu default
        return $actions
                ->add(Crud::PAGE_INDEX, Action::DETAIL)
        ;
}

public function configureCrud(Crud $crud): Crud
{
        // Customize the rendering of the row to grey-out the completed Todos
        return $crud
                ->overrideTemplate('crud/index', 'admin/crud/todo_index.html.twig')
        ;
}

D’autres paramétrages un peu spéciaux ont dû être faits pour éviter certains problèmes de mise au point (ou contourner des bugs) :

Dans config/packages/framework.yaml, modifier la valeur de cookie_secure à false :

framework:
    # ...
    session:
        # ...

        # make EasyAdmin happy, otherwise CSRF fails for weird reason
        cookie_secure: false

Notes de bas de page:

1

On verra plus tard dans le cours comment les technologies autour de CSS et Javascript permettent de faire fonctionner ce genre de changements d’aspect dans un document HTML.

Author: Olivier Berger (TSP)

Date: 2024-09-06 Fri 13:21

Emacs (Org mode)