Le cycle de vie d'une requête Symfony
Prendre du recul sur tout ce que vous avez manipulé jusqu'ici (routes, contrôleurs, vues, base de données) pour comprendre comment Symfony articule tout cela à chaque requête HTTP.
Notions théoriques
Le Front Controller
Depuis le début de ce cours, vous avez créé des routes, des contrôleurs et des vues Twig. Mais comment une simple URL tapée dans le navigateur finit-elle par exécuter la bonne méthode PHP ?
La réponse tient en un principe : le Front Controller (contrôleur frontal). Dans une application Symfony, toutes les requêtes HTTP passent par un unique point d'entrée : le fichier public/index.php.
Que vous visitiez /game/1, /player ou n'importe quelle autre URL de votre application, c'est toujours le même fichier public/index.php qui est exécuté en premier. C'est lui qui va ensuite déterminer quoi faire de la requête.
Ce principe évite d'avoir un fichier PHP différent par page (comme en PHP procédural classique), et permet à Symfony de mettre en place un traitement uniforme pour chaque requête : sécurité, journalisation, gestion des erreurs, etc.
Le rôle du Kernel
Le fichier public/index.php délègue immédiatement le travail à un objet central : le Kernel (le noyau de l'application), fourni par le composant HttpKernel.
Le rôle du Kernel est simple à formuler, même s'il cache une mécanique riche :
Le Kernel reçoit un objet Request (la requête HTTP entrante) et retourne un objet Response (la réponse HTTP à renvoyer).
Tout ce que fait Symfony entre les deux, ce sont les étapes que vous avez déjà pratiquées séparément dans les séances précédentes, sans forcément voir comment elles s'enchaînent.
Les grandes étapes du cycle
Voici le déroulement complet d'une requête, du navigateur jusqu'à la réponse affichée :
- Requête entrante : le navigateur envoie une requête HTTP (ex.
GET /game/1) qui arrive surpublic/index.php. - Le routeur analyse l'URL et détermine quelle route correspond, donc quel contrôleur (et quelle méthode) doit être appelé (voir la séance sur le routage).
- Exécution du contrôleur : Symfony appelle la méthode du contrôleur correspondante, en lui passant les paramètres nécessaires (comme l'
idextrait de l'URL). - Accès aux données (optionnel) : le contrôleur peut interroger la base de données via Doctrine pour récupérer les informations nécessaires (voir la séance sur Doctrine).
- Génération de la réponse : le contrôleur prépare les données et les transmet à un template Twig via
render(), qui génère le HTML final (voir la séance sur Twig). Le contrôleur peut aussi construire une réponse directement, sans passer par Twig. - Réponse renvoyée : le HTML généré est encapsulé dans un objet
Response, renvoyé au Kernel, puis envoyé au navigateur.
Navigateur
│ requête HTTP (GET /game/1)
▼
public/index.php (Front Controller)
│
▼
Kernel (HttpKernel)
│ reçoit un objet Request
▼
Routeur → détermine : GameController::show()
│
▼
Contrôleur (GameController::show)
│ interroge éventuellement la base de données (Doctrine)
│ transmet les données à un template Twig (render)
▼
Réponse HTML générée
│ encapsulée dans un objet Response
▼
Kernel → Navigateur
Les objets Request et Response
Ce cycle repose sur deux objets fournis par Symfony, dans le composant HttpFoundation :
Requestreprésente la requête HTTP entrante : elle contient l'URL, les paramètres GET ($request->query), les données POST ($request->request), les en-têtes, etc.Responsereprésente la réponse HTTP sortante : elle contient le contenu (souvent du HTML), le code de statut (ex.200,404) et des en-têtes.
Un contrôleur peut recevoir un objet Request en paramètre, et doit toujours retourner un objet Response — que ce Response soit construit à la main ou généré par $this->render() (qui retourne en réalité un Response contenant le HTML généré par Twig).
public function show(int $id, Request $request): Response
{
$format = $request->query->get('format', 'html');
// ... le contrôleur peut utiliser Doctrine, puis Twig ...
return $this->render('game/show.html.twig', ['id' => $id]);
}
La méthode render() que vous utilisez depuis la séance sur les vues ne fait pas exception à la règle : elle retourne bien un objet Response, dont le contenu a simplement été généré par le moteur Twig.
Exemple de mise en application
Reprenons le GameController du projet my_game. Ajoutons une méthode search qui lit un paramètre GET directement depuis l'objet Request, pour illustrer le cycle complet.
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
class GameController extends AbstractController
{
#[Route('/game/search', name: 'game_search')]
public function search(Request $request): Response
{
// Étape 1 : le routeur a déjà identifié cette méthode grâce à l'URL /game/search
// Étape 2 : on lit un paramètre transmis dans l'URL, ex. /game/search?q=echecs
$query = $request->query->get('q', '');
// Étape 3 : on pourrait interroger Doctrine ici pour chercher des jeux
// Étape 4 : on transmet le résultat à Twig, qui génère le HTML
return $this->render('game/search.html.twig', [
'query' => $query,
]);
}
}
Quand un utilisateur visite http://localhost:8000/game/search?q=echecs, voici ce qui se passe : public/index.php reçoit la requête, le Kernel la transforme en objet Request, le routeur identifie la méthode search() de GameController, cette méthode lit le paramètre q dans la Request, puis retourne un Response généré par Twig. Ce Response est renvoyé au navigateur.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Votre défi pour aujourd'hui consiste à ajouter au
PlayerControllerune méthode qui utilise explicitement l'objetRequest, pour bien identifier chaque étape du cycle vu en théorie.
Étape 1 — Importer la classe Request
Le contrôleur doit pouvoir recevoir un objet Request en paramètre de méthode. Ajoutez l'import correspondant en haut du fichier PlayerController.php.
Étape 2 — Ajouter un paramètre Request à la méthode index()
Modifiez la signature de la méthode index() pour qu'elle reçoive l'objet Request en plus des paramètres déjà présents.
Typer le paramètre Request $request permet à Symfony de reconnaître automatiquement
qu'il doit y injecter l'objet représentant la requête HTTP courante. C'est ce qu'on
appelle l'injection de dépendances : vous n'avez jamais besoin de créer vous-même
cet objet, Symfony s'en charge à chaque requête.
Étape 3 — Lire le paramètre GET "search" depuis la Request
Complétez la ligne qui lit le paramètre GET nommé search dans l'URL (ex. /player?search=alice), avec une valeur par défaut vide si le paramètre est absent.
Utiliser $request->query->get('search', '') évite une erreur si le paramètre search
n'est pas présent dans l'URL. Sans valeur par défaut, la méthode retournerait null,
ce qui peut provoquer des erreurs plus loin dans le code si celui-ci suppose une chaîne de caractères.