Créer un contrôleur
Notre projet de livre d'or est déjà en ligne sur les serveurs de production, mais nous avons un peu triché. Le projet n'a pas encore de page web. La page d'accueil est une ennuyeuse page d'erreur 404. Corrigeons cela.
Lorsqu'une requête HTTP arrive au serveur, comme pour notre page d'accueil (http://localhost:8000/
), Symfony essaie de trouver une route qui corresponde au chemin de la requête (/
ici). Une route est le lien entre le chemin de la requête et un callable PHP, une fonction devant créer la réponse HTTP associée à cette requête.
Ces callables sont nommés "contrôleurs". Dans Symfony, la plupart des contrôleurs sont implémentés sous la forme de classes PHP. Vous pouvez créer ces classes manuellement, mais comme nous aimons aller vite, voyons comment Symfony peut nous aider.
Se faciliter la vie avec le Maker Bundle
Pour générer des contrôleurs facilement, nous pouvons utiliser le paquet symfony/maker-bundle
, qui a été installé en tant que composant du paquet webapp
.
Le Maker Bundle vous permet de générer un grand nombre de classes différentes. Nous l'utiliserons constamment dans ce livre. Chaque "générateur" correspond à une commande et chacune d'entre elles appartient au même namespace make
.
La commande list
, intégrée nativement à la console symfony
, permet d'afficher toutes les commandes disponibles sous un namespace donné ; utilisez-la pour découvrir tous les générateurs fournis par le Maker Bundle :
1
$ symfony console list make
Choisir un format de configuration
Avant de créer le premier contrôleur du projet, nous devons décider des formats de configuration que nous voulons utiliser. Symfony supporte nativement YAML, XML, PHP et les attributs PHP.
Pour la configuration des paquets, YAML est le meilleur choix. C'est le format utilisé dans le répertoire config/
. Souvent, lorsque vous installez un nouveau paquet, la recette de ce paquet crée un nouveau fichier se terminant par .yaml
dans ce répertoire.
Pour la configuration liée au code PHP, les attributs sont plus appropriées, car elles cohabitent avec le code. Prenons un exemple : lorsqu'une requête arrive, la configuration doit indiquer à Symfony que le chemin de la requête doit être géré par un contrôleur spécifique (une classe PHP). Si notre configuration est en YAML, XML ou PHP, deux fichiers sont alors impliqués (le fichier de configuration et le contrôleur PHP). Avec les attributs, la configuration se fait directement dans le contrôleur.
Vous vous demandez peut-être comment vous pouvez deviner le nom du paquet à installer pour une fonctionnalité donnée ? La plupart du temps, vous n'avez pas besoin de le savoir, car Symfony propose le nom du paquet à installer dans ses messages d'erreur. Par exemple, exécuter symfony make:message
sans le paquet messenger
se terminerait par une exception contenant une indication sur le bon paquet à installer.
Générer un contrôleur
Créez votre premier Controller avec la commande make:controller
:
1
$ symfony console make:controller ConferenceController
La commande crée une classe ConferenceController
dans le répertoire src/Controller/
. La classe générée contient du code standard prêt à être ajusté :
L'attribut #[Route('/conference', name: 'conference')]
est ce qui fait de la méthode index()
un contrôleur (la configuration est à côté du code qu'elle configure).
Lorsque vous visitez la page /conference
dans un navigateur, le contrôleur est exécuté et une réponse est renvoyée.
Modifiez la route afin qu'elle corresponde à la page d'accueil :
1 2 3 4 5 6 7 8 9 10 11
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -8,7 +8,7 @@ use Symfony\Component\Routing\Annotation\Route;
class ConferenceController extends AbstractController
{
- #[Route('/conference', name: 'conference')]
+ #[Route('/', name: 'homepage')]
public function index(): Response
{
return $this->render('conference/index.html.twig', [
Le nom de la route (name
) sera utile lorsque nous voudrons faire référence à la page d'accueil dans notre code. Au lieu de coder en dur le chemin /
, nous utiliserons le nom de la route.
À la place de la page par défaut, retournons une simple page HTML :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -11,8 +11,13 @@ class ConferenceController extends AbstractController
#[Route('/', name: 'homepage')]
public function index(): Response
{
- return $this->render('conference/index.html.twig', [
- 'controller_name' => 'ConferenceController',
- ]);
+ return new Response(<<<EOF
+<html>
+ <body>
+ <img src="/images/under-construction.gif" />
+ </body>
+</html>
+EOF
+ );
}
}
Rafraîchissez le navigateur :
La responsabilité principale d'un contrôleur est de retourner une réponse HTTP (Response
) pour la requête.
Comme le reste du chapitre porte sur du code que nous ne conserverons pas, nous allons commiter nos modifications maintenant :
1 2
$ git add .
$ git commit -m'Add the index controller'
Ajouter un easter egg
Pour montrer comment une réponse peut tirer parti de l'information contenue dans la requête, ajoutons un petit easter egg. Lorsqu'une requête vers la page d'accueil sera réalisée avec un paramètre d'URL comme ?hello=Fabien
, nous ajouterons du texte pour saluer la personne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,17 +3,24 @@
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ConferenceController extends AbstractController
{
#[Route('/', name: 'homepage')]
- public function index(): Response
+ public function index(Request $request): Response
{
+ $greet = '';
+ if ($name = $request->query->get('hello')) {
+ $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
+ }
+
return new Response(<<<EOF
<html>
<body>
+ $greet
<img src="/images/under-construction.gif" />
</body>
</html>
Symfony expose les données de la requête à travers un objet Request
. Lorsque Symfony voit un argument de contrôleur avec ce typage précis, il sait automatiquement qu'il doit vous le passer. Nous pouvons l'utiliser pour récupérer le nom depuis le paramètre d'URL et ajouter un titre <h1>
.
Dans un navigateur, rendez-vous sur /
, puis sur /?hello=Fabien
pour constater la différence.
Note
Remarquez l'appel à htmlspecialchars()
, pour éviter les attaques XSS. Ce sera fait automatiquement pour nous lorsque nous passerons à un moteur de template digne de ce nom.
Nous aurions également pu inclure le nom directement dans l'URL :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -9,11 +9,11 @@ use Symfony\Component\Routing\Annotation\Route;
class ConferenceController extends AbstractController
{
- #[Route('/', name: 'homepage')]
- public function index(Request $request): Response
+ #[Route('/hello/{name}', name: 'homepage')]
+ public function index(string $name = ''): Response
{
$greet = '';
- if ($name = $request->query->get('hello')) {
+ if ($name) {
$greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
}
La partie de la route {name}
est un paramètre de route dynamique - il fonctionne comme un joker. Vous pouvez maintenant vous rendre sur /hello
et sur /hello/Fabien
dans un navigateur pour obtenir les mêmes résultats qu'auparavant. Vous pouvez récupérer la valeur du paramètre {name}
en ajoutant un argument portant le même nom au contrôleur, donc $name
.
Annulez les changements que nous venons juste de faire :
1
$ git checkout src/Controller/ConferenceController.php
1 2
$ git reset HEAD src/Controller/ConferenceController.php
$ git checkout src/Controller/ConferenceController.php
Débogguer des variables
La fonction dump()
est un utilitaire de déboggage très puissant. Elle est toujours disponible et vous permet de voir le contenu de variables complexes dans un format interactif.
Modifiez temporairement le fichier src/Controller/ConferenceController.php
pour afficher le contenu de l'objet Request :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,14 +3,17 @@
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ConferenceController extends AbstractController
{
#[Route('/', name: 'homepage')]
- public function index(): Response
+ public function index(Request $request): Response
{
+ dump($request);
+
return new Response(<<<EOF
<html>
<body>
Quand vous rafraichissez la page, une icône "cible" apparait dans la barre de déboggage; elle vous permet d'inspecter le dump. Cliquez dessus pour accéder à une page dédiée rendant la navigation plus simple :
Annulez les changements que nous venons juste de faire :
1
$ git checkout src/Controller/ConferenceController.php
1 2
$ git reset HEAD src/Controller/ConferenceController.php
$ git checkout src/Controller/ConferenceController.php
Aller plus loin
- Le système de routage de Symfony ;
- SymfonyCasts : tutoriels sur les routes, contrôleurs et pages ;
- Attributs en PHP ;
- Le composant HttpFoundation ;
- Attaques de sécurité XSS (Cross-Site Scripting) ;
- La cheat sheet du système de routage Symfony.