🛣️ Routing et Contrôleurs

Maîtriser le cœur de Symfony

Routes • Paramètres • Génération d'URLs • Request & Response

Formation DWWM - 2026

Au programme 📋

# 🗺️ Partie 1

Le Routing en Profondeur

Le GPS de votre application

C'est quoi le routing ? 🗺️

Le routing est le système qui associe une URL à un contrôleur.

Analogie : Le réceptionniste d'hôtel 🏨

Imagine un grand hôtel avec 100 chambres :

Sans réceptionniste, le client erre dans les couloirs. Sans routing, la requête HTTP est perdue !

Cycle de vie d'une requête HTTP 🔄

Voici le parcours COMPLET d'une requête dans Symfony :

  1. Utilisateur tape l'URL
    Exemple : http://localhost:8000/blog/123
  2. Serveur web reçoit la requête
    Apache/Nginx intercepte et appelle index.php
  3. Kernel Symfony démarre
    Charge tous les composants
  4. Router analyse l'URL
    Cherche quelle route correspond à /blog/123
  5. Route trouvée
    /blog/{id} → BlogController::show()
  6. Contrôleur exécuté
    La méthode show($id) est appelée avec id=123
  7. Réponse générée
    Le contrôleur retourne une Response
  8. HTTP Response envoyée
    Le navigateur reçoit le HTML

Les 3 composants d'une route 🎯

Une route Symfony est composée de 3 éléments essentiels :

Composant Description Exemple
1. Le PATH L'URL à matcher /blog/{id}
2. Le NAME Identifiant unique de la route blog_show
3. Le CONTROLLER Méthode à exécuter BlogController::show()

Exemple complet

#[Route('/blog/{id}', name: 'blog_show')]
public function show(int $id): Response
{
    // ...
}

// PATH: /blog/{id}
// NAME: blog_show
// CONTROLLER: BlogController::show()
# 🎯 Partie 2

Routes avec Attributs PHP

La méthode moderne

Attributs PHP 8 🆕

Depuis PHP 8 et Symfony 6, on utilise les attributs au lieu des annotations.

❌ Anciennes annotations (PHP 7)

/**
 * @Route("/blog/{id}", name="blog_show")
 */
public function show($id)
{
    // ...
}

Dans des commentaires DocBlock

✅ Nouveaux attributs (PHP 8+)

#[Route('/blog/{id}', name: 'blog_show')]
public function show(int $id): Response
{
    // ...
}

Syntaxe native PHP

✅ Avantages des attributs :

Syntaxe complète des attributs Route 📝

#[Route(
    path: '/blog/{id}',           // L'URL
    name: 'blog_show',            // Nom de la route
    requirements: ['id' => '\d+'], // Contraintes
    methods: ['GET'],              // Méthodes HTTP autorisées
    defaults: ['id' => 1],        // Valeurs par défaut
    priority: 0,                  // Priorité de la route
    condition: "context.getMethod() in ['GET', 'HEAD']"
)]
public function show(int $id): Response
{
    // ...
}

💡 Note : Vous n'utiliserez pas tous ces paramètres à chaque fois ! Les plus courants sont : path, name, requirements et methods.

Routes simples (sans paramètres) 🎯

Route de base

#[Route('/', name: 'home')]
public function index(): Response
{
    return new Response('Page d\'accueil');
}

Plusieurs routes pour la même méthode

#[Route('/', name: 'home')]
#[Route('/accueil', name: 'home_alt')]
#[Route('/index', name: 'home_index')]
public function index(): Response
{
    // Accessible par 3 URLs différentes !
    return new Response('Page d\'accueil');
}

✅ Cas d'usage : Utile pour gérer plusieurs langues ou URLs héritées d'un ancien site.

Préfixe de routes (au niveau classe) 🏷️

On peut définir un préfixe commun pour toutes les routes d'un contrôleur :

<?php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/blog')]  // ← Préfixe pour tout le contrôleur
class BlogController extends AbstractController
{
    #[Route('/', name: 'blog_index')]
    public function index(): Response
    {
        // URL finale : /blog/
    }
    
    #[Route('/{id}', name: 'blog_show')]
    public function show(int $id): Response
    {
        // URL finale : /blog/{id}
    }
    
    #[Route('/new', name: 'blog_new')]
    public function new(): Response
    {
        // URL finale : /blog/new
    }
}

Analogie : L'adresse postale 📮

Le préfixe de classe = la rue ("Rue des Lilas")

Chaque route = le numéro de maison (5, 10, 15...)

Adresse complète = "5 Rue des Lilas", "10 Rue des Lilas"...

# 📝 Partie 3

Paramètres d'URL

Rendre les routes dynamiques

Paramètres simples {parametre} 📦

Les paramètres se définissent entre accolades {} dans l'URL :

Exemple : Afficher un article par ID

#[Route('/article/{id}', name: 'article_show')]
public function show(int $id): Response
{
    return new Response("Article n°" . $id);
}

// /article/5   → "Article n°5"
// /article/123 → "Article n°123"
URL : /article/42
↓
Router extrait : {id} = 42
↓
Appelle show($id = 42)
↓
Affiche : "Article n°42"

💡 Important : Le nom du paramètre dans l'URL doit correspondre au nom de la variable dans la méthode !

Plusieurs paramètres 📦📦📦

On peut avoir autant de paramètres qu'on veut :

Exemple : Article d'une catégorie

#[Route('/blog/{category}/{slug}', name: 'blog_post')]
public function post(string $category, string $slug): Response
{
    return new Response(
        "Catégorie: $category, Article: $slug"
    );
}

// URL : /blog/tech/symfony-introduction
// Résultat : "Catégorie: tech, Article: symfony-introduction"

Exemple : Date dans l'URL

#[Route('/archive/{year}/{month}/{day}', name: 'archive')]
public function archive(int $year, int $month, int $day): Response
{
    return new Response(
        "Archives du $day/$month/$year"
    );
}

// URL : /archive/2025/01/28
// Résultat : "Archives du 28/01/2025"

Paramètres optionnels (avec valeur par défaut) ⚙️

Un paramètre peut avoir une valeur par défaut :

Méthode 1 : Valeur par défaut dans la méthode

#[Route('/blog/page/{page}', name: 'blog_page')]
public function page(int $page = 1): Response
{
    return new Response("Page n°$page");
}

// /blog/page/5  → "Page n°5"
// /blog/page/   → "Page n°1"  (valeur par défaut)
// /blog/page    → Erreur 404 (URL non complète)

Méthode 2 : Valeur par défaut dans l'attribut Route

#[Route('/blog/{page}', name: 'blog_page', defaults: ['page' => 1])]
public function page(int $page): Response
{
    return new Response("Page n°$page");
}

// /blog/5  → "Page n°5"
// /blog    → "Page n°1"  (valeur par défaut)

✅ Différence : La méthode 2 accepte l'URL sans le paramètre (/blog), alors que la méthode 1 nécessite le slash final (/blog/)

Requirements - Contraintes sur les paramètres 🔒

On peut forcer un paramètre à respecter un format avec requirements :

Contraindre à des chiffres uniquement

#[Route('/article/{id}', name: 'article_show', requirements: ['id' => '\d+'])]
public function show(int $id): Response
{
    // $id sera TOUJOURS un nombre
}

// ✅ /article/123  → OK
// ❌ /article/abc  → 404

Contraindre à un format slug

#[Route('/blog/{slug}', name: 'blog_post', requirements: ['slug' => '[a-z0-9-]+'])]
public function post(string $slug): Response
{
    // Accepte seulement lettres minuscules, chiffres et tirets
}

// ✅ /blog/mon-article-123  → OK
// ❌ /blog/Mon_Article!     → 404

Contraindre à des valeurs spécifiques

#[Route('/langue/{locale}', name: 'change_locale', 
    requirements: ['locale' => 'fr|en|es'])]
public function changeLocale(string $locale): Response
{
    // Accepte SEULEMENT : fr, en, es
}

// ✅ /langue/fr  → OK
// ✅ /langue/en  → OK
// ❌ /langue/de  → 404

Regex courantes pour requirements 📋

Type de donnée Regex Exemple valide
Nombre entier \d+ 123, 456
Slug (URL friendly) [a-z0-9-]+ mon-article-123
UUID [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} 550e8400-e29b-41d4-a716-446655440000
Date YYYY-MM-DD \d{4}-\d{2}-\d{2} 2025-01-28
Code postal FR \d{5} 75001
Langue (2 lettres) [a-z]{2} fr, en

⚠️ Important : Si aucune route ne matche, Symfony retourne une erreur 404. Les requirements protègent aussi contre les injections !

Méthodes HTTP autorisées 🔐

On peut limiter une route à certaines méthodes HTTP :

Route GET uniquement

#[Route('/article/{id}', name: 'article_show', methods: ['GET'])]
public function show(int $id): Response
{
    // Accessible SEULEMENT en GET
}

Route POST uniquement (formulaire)

#[Route('/article/create', name: 'article_create', methods: ['POST'])]
public function create(Request $request): Response
{
    // Accessible SEULEMENT en POST
}

GET et POST

#[Route('/contact', name: 'contact', methods: ['GET', 'POST'])]
public function contact(Request $request): Response
{
    if ($request->isMethod('POST')) {
        // Traiter le formulaire
    }
    // Afficher le formulaire
}

Méthodes HTTP courantes :

# 🔗 Partie 4

Génération d'URLs

Ne jamais écrire d'URLs en dur

Pourquoi générer les URLs ? 🤔

Analogie : Les signets de livre 📖

Imagine que tu as un livre de 500 pages :

❌ Mauvaise méthode : Tu écris "page 243" partout dans tes notes. Si les pages changent lors d'une réédition, toutes tes notes sont fausses !

✅ Bonne méthode : Tu notes "Chapitre 12, Section 3". Même si la pagination change, tu retrouves toujours le bon endroit.

❌ URLs en dur (MAUVAIS)

<a href="/blog/123">Article</a>

<!-- Si tu changes la route en /article/123,
     tous tes liens sont cassés ! -->

✅ URLs générées (BON)

<a href="{{ path('blog_show', {id: 123}) }}">
    Article
</a>

<!-- Si tu changes l'URL, les liens
     se mettent à jour automatiquement ! -->

path() dans Twig 🎨

La fonction path() génère une URL relative :

Route sans paramètres

{{ path('home') }}
{# Génère : / #}

<a href="{{ path('home') }}">Accueil</a>

Route avec 1 paramètre

{{ path('blog_show', {id: 123}) }}
{# Génère : /blog/123 #}

<a href="{{ path('blog_show', {id: article.id}) }}">
    Lire l'article
</a>

Route avec plusieurs paramètres

{{ path('blog_post', {category: 'tech', slug: 'symfony-intro'}) }}
{# Génère : /blog/tech/symfony-intro #}

<a href="{{ path('blog_post', {
    category: article.category,
    slug: article.slug
}) }}">
    {{ article.title }}
</a>

url() vs path() dans Twig 🔗

Fonction Résultat Usage
path() URL relative
/blog/123
Liens internes au site
url() URL absolue
http://localhost:8000/blog/123
Emails, flux RSS, Open Graph

Exemples pratiques

{# Lien interne - utilise path() #}
<a href="{{ path('contact') }}">Contactez-nous</a>

{# Email - utilise url() #}
<p>Consultez cet article : {{ url('blog_show', {id: 123}) }}</p>

{# Open Graph (réseaux sociaux) - utilise url() #}
<meta property="og:url" content="{{ url('blog_show', {id: article.id}) }}">

💡 Règle : Utilise path() par défaut. N'utilise url() que si tu as besoin de l'URL complète (emails, API, métadonnées).

Générer des URLs dans le contrôleur 🎮

Dans un contrôleur, on utilise $this->generateUrl() :

URL relative

$url = $this->generateUrl('blog_show', ['id' => 123]);
// Résultat : /blog/123

URL absolue

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

$url = $this->generateUrl(
    'blog_show',
    ['id' => 123],
    UrlGeneratorInterface::ABSOLUTE_URL
);
// Résultat : http://localhost:8000/blog/123

Exemple : Envoyer un email avec lien

public function sendEmail(MailerInterface $mailer): Response
{
    $articleUrl = $this->generateUrl(
        'blog_show',
        ['id' => 123],
        UrlGeneratorInterface::ABSOLUTE_URL
    );
    
    $email = (new Email())
        ->to('user@example.com')
        ->subject('Nouvel article')
        ->html("Lire l'article : <a href='$articleUrl'>Cliquez ici</a>");
    
    $mailer->send($email);
    
    return new Response('Email envoyé !');
}
# 🎮 Partie 5

AbstractController

Votre boîte à outils

C'est quoi AbstractController ? 🧰

AbstractController est la classe parent de tous vos contrôleurs. Elle fournit des méthodes utiles.

Analogie : La boîte à outils du bricoleur 🔧

Quand tu hérites d'AbstractController, c'est comme si on te donnait une boîte à outils complète :

Sans héritage, tu devrais créer tous ces outils toi-même !

Méthodes essentielles d'AbstractController 🛠️

Méthode Description Exemple
render() Rendre un template Twig $this->render('home.html.twig')
json() Retourner du JSON $this->json(['status' => 'ok'])
redirectToRoute() Rediriger vers une route $this->redirectToRoute('home')
redirect() Rediriger vers une URL $this->redirect('https://google.com')
file() Télécharger un fichier $this->file('document.pdf')
addFlash() Ajouter un message flash $this->addFlash('success', 'OK!')
generateUrl() Générer une URL $this->generateUrl('home')
createNotFoundException() Créer une erreur 404 throw $this->createNotFoundException()

render() - Rendre un template 🎨

La méthode render() génère du HTML à partir d'un template Twig.

Syntaxe complète

$this->render(
    string $view,              // Chemin du template
    array $parameters = [],    // Variables à passer
    ?Response $response = null // Response existante (rare)
): Response

Exemples

// Template simple
return $this->render('home/index.html.twig');

// Avec variables
return $this->render('blog/show.html.twig', [
    'article' => $article,
    'comments' => $comments
]);

// Modifier le statut HTTP
return $this->render('error.html.twig', [], 
    new Response('', 500)
);

💡 Note : render() retourne toujours un objet Response avec le HTML généré et un statut HTTP 200.

json() - Retourner du JSON 📊

La méthode json() convertit automatiquement des données PHP en JSON.

Syntaxe

$this->json(
    mixed $data,           // Données à convertir
    int $status = 200,     // Code HTTP
    array $headers = [],   // Headers personnalisés
    array $context = []    // Options de serialization
): JsonResponse

Exemples

// Tableau simple
return $this->json(['message' => 'Success', 'data' => [1, 2, 3]]);

// Objet
return $this->json($user);

// Avec statut HTTP personnalisé
return $this->json(['error' => 'Not found'], 404);

// Avec headers CORS
return $this->json($data, 200, [
    'Access-Control-Allow-Origin' => '*'
]);

✅ Avantages :

# 📥 Partie 6

Request et Response

Communication client-serveur

L'objet Request 📨

Request contient TOUTES les informations de la requête HTTP entrante.

Analogie : Le colis postal 📦

Quand tu reçois un colis :

Request = tout ce qu'il y a sur et dans le colis !

Propriétés de l'objet Request 📋

Propriété Contenu Exemple
$request->query Paramètres GET (?key=value) $request->query->get('page')
$request->request Paramètres POST (formulaire) $request->request->get('email')
$request->files Fichiers uploadés $request->files->get('photo')
$request->cookies Cookies $request->cookies->get('session_id')
$request->headers Headers HTTP $request->headers->get('User-Agent')
$request->server Variables serveur $request->server->get('REMOTE_ADDR')

Récupérer des données GET 📥

Les paramètres GET viennent de l'URL après le ?

URL : /search?q=symfony&page=2

use Symfony\Component\HttpFoundation\Request;

#[Route('/search', name: 'search')]
public function search(Request $request): Response
{
    // Récupérer un paramètre
    $query = $request->query->get('q');
    $page = $request->query->get('page');
    
    // Avec valeur par défaut
    $limit = $request->query->get('limit', 10);
    
    // Tous les paramètres GET
    $allParams = $request->query->all();
    
    return new Response("Recherche: $query, Page: $page");
}

// URL : /search?q=symfony&page=2
// Résultat : "Recherche: symfony, Page: 2"

💡 Note : get('key', 'default') retourne la valeur par défaut si la clé n'existe pas.

Récupérer des données POST 📮

Les paramètres POST viennent d'un formulaire soumis.

#[Route('/login', name: 'login', methods: ['GET', 'POST'])]
public function login(Request $request): Response
{
    if ($request->isMethod('POST')) {
        // Récupérer les champs du formulaire
        $email = $request->request->get('email');
        $password = $request->request->get('password');
        
        // Traiter le login...
        return new Response("Login: $email");
    }
    
    // Afficher le formulaire
    return $this->render('login.html.twig');
}

Vérifier la méthode HTTP

if ($request->isMethod('POST')) { /* ... */ }
if ($request->isMethod('GET')) { /* ... */ }

// Ou
$method = $request->getMethod(); // 'GET', 'POST', etc.

Récupérer les headers HTTP 📨

// User-Agent
$userAgent = $request->headers->get('User-Agent');

// Accept-Language
$language = $request->headers->get('Accept-Language');

// Referer (page précédente)
$referer = $request->headers->get('referer');

// Content-Type
$contentType = $request->headers->get('Content-Type');

// Tous les headers
$allHeaders = $request->headers->all();

Cas pratique : Détecter le navigateur

public function detectBrowser(Request $request): Response
{
    $userAgent = $request->headers->get('User-Agent');
    
    if (str_contains($userAgent, 'Chrome')) {
        $browser = 'Google Chrome';
    } elseif (str_contains($userAgent, 'Firefox')) {
        $browser = 'Mozilla Firefox';
    } else {
        $browser = 'Autre navigateur';
    }
    
    return new Response("Vous utilisez : $browser");
}

L'objet Response 📤

Response est ce que vous renvoyez au client (navigateur).

Anatomie d'une Response HTTP 🧬

HTTP/1.1 200 OK                    ← Statut HTTP
Content-Type: text/html            ← Headers
Set-Cookie: session=abc123
                                   ← Ligne vide
<html>                             ← Body (contenu)
  <body>Hello World</body>
</html>

Créer une Response simple

use Symfony\Component\HttpFoundation\Response;

return new Response(
    '<html><body>Hello</body></html>',  // Contenu
    200,                                      // Statut HTTP
    ['Content-Type' => 'text/html']          // Headers
);

Codes de statut HTTP 🔢

Code Signification Usage
200 OK Requête réussie (par défaut)
201 Created Ressource créée (API)
204 No Content Succès sans contenu (API)
301 Moved Permanently Redirection permanente
302 Found Redirection temporaire
400 Bad Request Requête invalide
401 Unauthorized Non authentifié
403 Forbidden Accès interdit
404 Not Found Ressource introuvable
500 Internal Server Error Erreur serveur

Types de Response spécialisées 🎭

1. Response HTML classique

return new Response('<h1>Hello</h1>');

2. JsonResponse (API)

use Symfony\Component\HttpFoundation\JsonResponse;

return new JsonResponse(['message' => 'Success', 'data' => [1, 2, 3]]);

3. RedirectResponse

use Symfony\Component\HttpFoundation\RedirectResponse;

return new RedirectResponse('https://google.com');

4. BinaryFileResponse (téléchargement)

use Symfony\Component\HttpFoundation\BinaryFileResponse;

return new BinaryFileResponse('/path/to/document.pdf');

5. StreamedResponse (gros fichiers)

use Symfony\Component\HttpFoundation\StreamedResponse;

return new StreamedResponse(function() {
    echo 'Chunk 1';
    flush();
    sleep(1);
    echo 'Chunk 2';
});
# ↩️ Partie 7

Redirections et Forward

Naviguer entre les pages

redirectToRoute() - Redirection vers une route 🔄

La méthode la plus courante pour rediriger :

Syntaxe

$this->redirectToRoute(
    string $route,           // Nom de la route
    array $parameters = [],  // Paramètres de la route
    int $status = 302        // Code HTTP (302 par défaut)
): RedirectResponse

Exemples

// Redirection simple
return $this->redirectToRoute('home');

// Avec paramètres
return $this->redirectToRoute('blog_show', ['id' => 123]);

// Redirection permanente (301)
return $this->redirectToRoute('new_url', [], 301);

Cas pratique : Après création d'article

#[Route('/article/create', name: 'article_create', methods: ['POST'])]
public function create(Request $request): Response
{
    // Créer l'article...
    $articleId = 123;
    
    // Rediriger vers la page de l'article créé
    return $this->redirectToRoute('article_show', ['id' => $articleId]);
}

redirect() - Redirection vers une URL 🌐

Pour rediriger vers une URL externe ou complète :

Exemples

// Vers un site externe
return $this->redirect('https://www.google.com');

// Vers une URL relative
return $this->redirect('/old-page');

// Génération d'URL puis redirection
$url = $this->generateUrl('blog_show', ['id' => 123]);
return $this->redirect($url);

⚠️ Important : Préférez toujours redirectToRoute() pour les redirections internes. N'utilisez redirect() que pour les URLs externes.

Redirection 301 vs 302 🔀

Code Type Comportement Usage
302 Found (Temporaire) Les moteurs de recherche gardent l'ancienne URL Redirection après formulaire, login temporaire
301 Moved Permanently Les moteurs de recherche indexent la nouvelle URL Changement d'URL définitif, refonte de site

Exemples

// 302 - Temporaire (par défaut)
return $this->redirectToRoute('home');

// 301 - Permanente
return $this->redirectToRoute('new_home', [], 301);

💡 SEO : Utilisez 301 quand vous changez définitivement une URL pour conserver votre référencement Google !

# ❌ Partie 8

Gestion des erreurs 404

Quand la ressource n'existe pas

createNotFoundException() 🚫

Lance une exception qui génère une page 404 :

Syntaxe

throw $this->createNotFoundException(string $message = 'Not Found');

Exemple : Article introuvable

#[Route('/article/{id}', name: 'article_show')]
public function show(int $id): Response
{
    $article = $this->getArticleById($id);
    
    if (!$article) {
        throw $this->createNotFoundException(
            'Article n°' . $id . ' introuvable'
        );
    }
    
    return $this->render('article/show.html.twig', [
        'article' => $article
    ]);
}

💡 Comportement :

Personnaliser la page 404 🎨

Créez un template personnalisé pour vos erreurs 404 :

Créer le template

📄 templates/bundles/TwigBundle/Exception/error404.html.twig

{% extends 'base.html.twig' %}

{% block title %}Page non trouvée{% endblock %}

{% block body %}
    <div style="text-align: center; padding: 4rem;">
        <h1 style="font-size: 6rem; color: #dc3545;">404</h1>
        <h2>Page non trouvée</h2>
        <p>La page que vous recherchez n'existe pas.</p>
        <a href="{{ path('home') }}">Retour à l'accueil</a>
    </div>
{% endblock %}

⚠️ Important : Cette page personnalisée s'affiche UNIQUEMENT en mode production (APP_ENV=prod). En dev, vous voyez toujours la page de debug.

# 🎓 Récapitulatif

Ce qu'on a appris

Résumé complet 📚

Concept Description Exemple
Routing Associe URL → Contrôleur #[Route('/blog')]
Paramètres Valeurs dynamiques dans l'URL /blog/{id}
Requirements Contraintes sur paramètres ['id' => '\d+']
path() Générer une URL path('blog_show', {id: 123})
Request Données de la requête HTTP $request->query->get('page')
Response Réponse au client new Response('Hello')
redirectToRoute() Rediriger vers une route redirectToRoute('home')
404 Page non trouvée createNotFoundException()

Points clés à retenir 🎯

  1. Toujours nommer vos routes
    Le name est obligatoire pour générer des URLs
  2. Utilisez path() pour les URLs
    Jamais d'URLs en dur comme /blog/123
  3. Contraignez vos paramètres
    requirements protège contre les valeurs invalides
  4. Request contient TOUT
    GET, POST, headers, cookies, files...
  5. Héritez d'AbstractController
    Accès aux méthodes render(), json(), etc.
  6. Redirigez après POST
    Pattern POST-Redirect-GET pour éviter la double soumission
  7. Gérez les 404 proprement
    createNotFoundException() + template personnalisé

Commandes utiles 🛠️

Action Commande
Lister toutes les routes php bin/console debug:router
Chercher une route php bin/console debug:router blog_show
Créer un contrôleur php bin/console make:controller
Vérifier une URL php bin/console router:match /blog/123

Exemple de debug

# Voir les détails d'une route
php bin/console debug:router blog_show

# Résultat :
# Name         blog_show
# Path         /blog/{id}
# Controller   App\Controller\BlogController::show()
# Requirements id: \d+
# Methods      GET

Erreurs courantes à éviter ⚠️

❌ Erreur 1 : Oublier le name

#[Route('/blog')]  // ❌ Pas de name
public function index() { }

#[Route('/blog', name: 'blog_index')]  // ✅ Avec name
public function index() { }

❌ Erreur 2 : URLs en dur

<a href="/blog/123">Article</a>  {# ❌ #}

<a href="{{ path('blog_show', {id: 123}) }}">Article</a>  {# ✅ #}

❌ Erreur 3 : Pas de contraintes

#[Route('/blog/{id}')]  // ❌ Accepte /blog/abc
public function show($id) { }

#[Route('/blog/{id}', requirements: ['id' => '\d+'])]  // ✅
public function show(int $id) { }

Bonnes pratiques 💡

  1. Préfixez vos noms de routes
    blog_index, blog_show, blog_create
  2. Groupez par fonctionnalité
    Un contrôleur par ressource (Blog, User, Product...)
  3. Utilisez les méthodes HTTP
    GET pour lire, POST pour créer, PUT pour modifier, DELETE pour supprimer
  4. Validez TOUJOURS les paramètres
    Requirements + vérification que la ressource existe
  5. Typez vos paramètres
    int $id au lieu de $id
  6. Séparez logique et présentation
    Logique dans le contrôleur, HTML dans Twig
  7. Gérez les cas d'erreur
    404, 403, 500... toujours prévoir le pire !

Ressources pour aller plus loin 📚

Documentation Symfony

🌐 https://symfony.com/doc/current/routing.html

Guide complet sur le routing

Composant HttpFoundation

🌐 https://symfony.com/doc/current/components/http_foundation.html

Tout sur Request et Response

Best Practices

🌐 https://symfony.com/doc/current/best_practices.html

Bonnes pratiques officielles Symfony

🎓 Bravo !

Vous maîtrisez le Routing et les Contrôleurs

Vous savez maintenant :

✅ Créer des routes avec attributs PHP

✅ Gérer les paramètres d'URL

✅ Générer des URLs dynamiques

✅ Utiliser Request et Response

✅ Rediriger proprement

✅ Gérer les erreurs 404

Prochaine étape : Twig ! 🎨

Formation DWWM - 2026