Une première application
La structure de Symfony
Toute l’application sera développée sous le dossier src. C’est ici, principalement que nous coderons toute notre application.
Le framework Symfony utilise une structure bien connue : la structure MVC. MVC signifie : Modèle, Vue, Contrôleur
Détaillons un peu ces trois éléments de structure.
-
Les modèles regroupent toutes les données qu’utilise votre application. Cela peut être des données issues de bases de données, mais pas uniquement. Les données peuvent provenir d’horizons différents et même être issues de calculs mathématiques. On dit souvent que le modèle est votre cœur de métier. C’est la matière sur laquelle vous travaillez. Nous verrons par la suite où installer les modèles. Nous verrons également comment utiliser Doctrine, le gestionnaire de bases de données de Symfony.
-
Les vues regroupent tout ce qui génère les pages HTML finales visibles par l’utilisateur. Ces vues sont toutes regroupées dans le dossier templates, qui est à la racine de votre application. Les vues ne sont pas des fichiers PHP, mais des fichiers Twig. Twig est un langage de template, nous y reviendrons.
-
Les contrôleurs regroupent tous les programmes PHP qui vont coordonner votre application. Ce sont eux qui vont appeler...
Les contrôleurs
Les contrôleurs seront tous créés dans le sous-dossier : src/Controller. Pour l’instant, ce dossier est vide.
-
Les contrôleurs sont des classes contenues dans un fichier du même nom.
-
Tous les noms de contrôleurs se terminent par Controller (exemple HomeController). Attention à la casse qui est importante : chaque mot commence par une majuscule et aucun séparateur n’est présent.
-
Il est possible de créer un contrôleur à la main, mais le mieux est d’utiliser la console de Symfony en ligne de commande ([Ctrl] ù sur VSCode).
Créons par exemple un contrôleur TestController, pour tester notre application. Sur la fenêtre du terminal, tapez la commande :
php bin/console make:controller TestController
Il se peut que vous obteniez le message d’erreur suivant :
Dans ce cas, votre installation n’a pas intégré une librairie qui s’appelle Symfony MakerBundle. Il faut bien sûr utiliser Composer pour l’installer :
composer require symfony/maker-bundle --dev
L’option dev va intégrer cette librairie dans l’étiquette require-dev du fichier composer.json. Nous pourrons retirer cette étiquette en mode production.
Vérifiez également que vous êtes bien en mode dev dans le fichier .env :
APP_ENV=dev
Relancez la commande :
php bin/console make:controller TestController
Cette fois, tout devrait bien se passer.
Le contrôleur TestController est créé dans le dossier src/Controller. Examinons...
Les vues
La vue est le fichier final qui génère la page destinée à être vue par l’utilisateur.
Elle se compose donc de HTML, de CSS, de JavaScript et de tous les éléments qui seront interprétés par le navigateur du client.
La vue peut contenir aussi de petites instructions d’un langage de template qui s’appelle Twig. Ce langage permet de faire des traitements dans la vue comme nous le ferions dans une page PHP. Par exemple, Twig permet de définir des variables, de faire des boucles, de mettre des instructions conditionnelles, mais beaucoup plus facilement qu’avec PHP. Le but est de séparer la partie développement PHP de la partie Front en HTML, CSS...
Ainsi, un intégrateur qui maîtrise Twig n’a pas besoin de développer en PHP pour mettre en place le design du site.
Toutes les vues sont rangées dans le dossier templates de l’application. Actuellement, le dossier templates contient un sous-dossier test qui a été généré par la création du contrôleur TestController. Il contient la vue index.html.twig.
Nous remarquons la présence d’un autre fichier : base.html.twig, dans le dossier templates. Il s’agit de ce qu’on appelle un layout. C’est une vue qui servira de squelette aux autres vues (voir chapitre Le moteur de template Twig).
Ouvrons...
Le dossier public
C’est le seul dossier accessible par la requête client. Il contient tous les fichiers pouvant être chargés sur le navigateur de l’utilisateur.
Il est important, si ce n’est pas le cas, de donner des droits en lecture à tout le monde à ce dossier (avec la nouvelle version de Symfony, il n’y a plus de problèmes à ce niveau-là).
Nous y trouverons donc le CSS pour les styles de la page, le JavaScript pour les animations, les images, plus largement tous les médias et les fichiers accessibles à l’utilisateur. Oui, tous ces fichiers se placent ici et non dans le dossier templates.
C’est ici également que se trouve ce qu’on appelle le point d’entrée de l’application, le fameux fichier index.php.
Toutes les requêtes de l’utilisateur, par exemple localhost:8000/test, n’accèdent qu’à ce fichier, qui transmettra par la suite l’action du contrôleur à exécuter.
Nous ne modifions pratiquement jamais le contenu de ce fichier.
Le dossier var
Ce dossier contient deux sous-dossiers, cache et logs.
Symfony met un certain nombre de fichiers en cache à chaque requête. Le sous-dossier cache contient tous les fichiers mis en cache par l’application.
Il sera nécessaire de vider régulièrement le cache, notamment pour prendre en compte les mises à jour du code. Il est possible de supprimer manuellement les sous-dossiers du dossier cache, comme par exemple, le sous-dossier dev.
Il existe également une commande de la console permettant de le faire :
php bin/console cache:clear
Si vous ne voyez pas le résultat d’une modification de votre code, c’est probablement dû au cache. Pensez à le vider régulièrement.
Le dossier vendor
C’est le dossier qui contient toutes les librairies utilisées par Symfony. Il est généré par Composer à l’installation. Comme nous l’avons vu, c’est Composer qui permet d’installer toutes les librairies nécessaires. Composer crée un dossier vendor et installe les librairies à l’intérieur. Il crée également un fichier autoload.php, qui permettra de faire référence à ces librairies dans le code grâce à l’espace de nom (namespace). Le chapitre Les outils de gestion de dépendances a été consacré à Composer.
À aucun moment vous ne devez modifier manuellement le contenu du dossier vendor. Celui-ci doit contenir la dernière version des librairies installées. Si une nouvelle librairie est installée par Composer, elle viendra s’ajouter aux autres dans ce dossier. Chaque mise à jour de Symfony viendra écraser le contenu de ce dossier. Nous ne nous en préoccupons donc pas.
Les autres fichiers de l’application
Nous reviendrons par la suite sur les autres dossiers constituant l’application. Il n’est pas difficile d’imaginer que le dossier translations va contenir les éventuels fichiers de traductions dans le cas d’un site multilingue et que le dossier test va permettre de définir des tests d’exécution de notre application.
Vous trouverez également le dossier bin, qui contient le fichier console, permettant d’exécuter des traitements en ligne de commande. Ce fichier est en PHP, ce qui signifie que nous pourrions également exécuter la console directement sur un navigateur.
Le dossier bin contient aussi un fichier phpunit utilisé pour effectuer des tests.
Enfin, à la racine de l’application, outre .env et .env.test, vous trouverez les fichiers composer.json, et composer.lock, qui verrouille les versions des librairies installées. Vous y trouverez également quelques autres fichiers (phpunit.xml.dist, symfony.lock…).
Voilà pour une présentation sommaire de la structure de Symfony.
Les composants de HttpFoundation
Tout accès à une application web se fait via une requête HTTP. Sans entrer dans les détails, une requête HTTP se compose d’un en-tête, qui contient des informations (comme par exemple : le nom de domaine du site, le type du contenu transmis…), et d’un corps (body) dans lequel sont passés les paramètres à transmettre.
La réponse du serveur suite à l’exécution de la requête a la même syntaxe. Elle contient un en-tête spécifiant également un certain nombre d’informations et le code HTML de la réponse qui sera interprété par le navigateur.
Ces deux éléments, avec toutes leurs informations, sont traduits dans Symfony par des composants de la bibliothèque HttpFoundation. Ces composants sont deux classes existantes : la classe Request et la classe Response. Reportez-vous au chapitre Le langage Objet, section Les classes, si vous avez oublié la notion de classe.
L’utilisation de ces deux classes Request et Responseest très pratique, car nous pouvons y retrouver aisément les paramètres de la requête transmise ou définir les paramètres de la réponse transmise.
Mais comment accéder à ces deux classes de Symfony ?
Il est possible d’y accéder en utilisant leur espace de noms...
L’objet Request
Nous parlerons d’objet Request, mais c’est un abus de langage. Il s’agit en fait d’un objet instancié à partir de la classe Request.
Pour utiliser l’objet Request dans une action, il est nécessaire de transmettre cet objet dans les paramètres de l’action.
Prenons l’exemple de l’action index() du TestController. Pour utiliser l’objet Request, il faut tout d’abord utiliser son espace de noms :
use Symfony\Component\HttpFoundation\Response;
puis, vous pouvez utiliser l’objet $request en injection de dépendance dans la méthode index :
use Symfony\Component\HttpFoundation\Request;
class TestController extends AbstractController
{
#[Route('/test', name: 'app_test')]
public function index(Request $request)
{... }
Cette utilisation peut apparaître étrange au premier abord.
Comment l’objet $request est-il instancié ?
L’objet $request est automatiquement instancié, parce qu’il est précédé du nom de la classe Request. C’est Symfony qui s’occupe de tout.
À l’intérieur de l’action, vous avez tout le loisir de consulter cet objet $request. Par exemple, pour récupérer le nom de la requête, il est possible d’utiliser la méthode getPathInfo().
Vous pouvez tester cette méthode en incluant le code suivant dans votre contrôleur TestController :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class TestController extends AbstractController
{
#[Route('/test', name: 'app_test')] ...
L’objet Response
Nous parlerons d’objet Response, mais c’est un abus de langage. Il s’agit en fait d’un objet instancié à partir de la classe Response.
L’objet Response définit la réponse à envoyer au navigateur client.
Contrairement à l’objet Request, il faut l’instancier à l’intérieur de l’action du contrôleur. Voici un exemple d’utilisation de l’objet Response :
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class TestController extends AbstractController
{
/**
* @Route("/test", name="test")
*/
public function index(Request $request)
{
$reponse=new Response('Bienvenue dans Symfony');
return $reponse;
}
}
L’action index() retourne la réponse, qui suivra son cours pour être transformée en réponse HTTP. Cette fois, nous n’avons pas besoin de la méthode render() qui fait appel à une page Twig.
Une action doit obligatoirement retourner un objet de la classe Response.
En exécutant la requête localhost:8000/test, vous obtiendrez l’affichage suivant :
Comment cela se passe-t-il quand l’action fait appel à une vue Twig ?
C’est la méthode $this->render(...) qui génère l’objet Response envoyé.
Un objet Response est donc bien retourné systématiquement par l’action. Attention : si vous omettez de renvoyer cet objet Response, vous aurez une erreur Symfony. Le typage impose que toutes les actions du contrôleur renvoient un objet de type Response.
La méthode $this->render se trouve dans la classe AbstractController. En consultant le contenu de cette classe dans le dossier : vendor/symfony/framework-bundle/controller, nous trouvons la méthode render, qui retourne un objet Response :
protected function render(string $view, array $parameters = [],
Response $response = null): Response
{
$content = $this->renderView($view, $parameters);
$response ??= new Response();
if...
Les variables de session
1. L’utilité des variables de session
Lorsqu’un client fait une requête HTTP, elle est réceptionnée par le serveur qui la traite et renvoie une réponse au navigateur du client.
Chaque requête est indépendante des autres.
Il n’y a pas d’information qui persiste d’une requête à l’autre. Les paramètres et tout ce que vous pouvez envoyer au serveur ne sont utilisables que dans la requête qui les contient.
Il est parfois nécessaire de conserver des informations propres au client qui est connecté. Par exemple, si votre client fait une commande de produit, il serait bien de conserver l’état de son panier de commande d’une requête à l’autre.
Cet état est propre à chaque client. Même si les informations sont sauvegardées sur le serveur (en base de données par exemple), il faut pouvoir identifier celles qui correspondent au client connecté.
Il faut donc pouvoir identifier chaque client connecté.
Pour ce faire, le serveur envoie un identifiant de session, qui est stocké sur le navigateur de chaque client (c’est un cookie). Grâce à cet identifiant, il peut repérer le client à chaque fois qu’il se connecte et lui afficher son panier de commande :
Le stockage de cet identifiant de session dure tant que le navigateur est ouvert. Il disparaît à la fermeture du navigateur. Si le client se reconnecte, il devra faire une nouvelle demande d’identifiant. Cette volatilité de l’identifiant de session permet d’éviter des failles de sécurité comme le vol de l’identifiant de session.
2. L’utilisation des variables de session sous Symfony
Les variables de session sont gérées à partir de l’objet $request. Il faut faire une demande d’ouverture d’une session avec l’instruction :
$session = $request->getSession();
Il est possible ensuite de créer une ou plusieurs variables de session. Ces variables seront identifiées comme étant propres au client grâce à l’identifiant de session généré :
$maVariable = $session->set('nom_de_ma_variable', 'valeur');
Il est possible dans toute l’application de récupérer la valeur des variables de session :
$maVariable = $session->get('nom_de_ma_variable');...