🐘 Symfony – Le Framework PHP Professionnel
Guide complet IDEO-Lab : Bundles, DI Container, Doctrine (ORM), Twig, Security & API Platform.
Vue d'ensemble
Framework PHP "full-stack" (ou "micro"). Basé sur des Composants.
Framework PHPPourquoi Symfony ?
Robuste (entreprise), Flexible (Composants), Standard (PSR), Performance.
Composants PSRInstallation (symfony CLI)
composer (paquets), symfony (CLI). symfony new, symfony server.
Structure (Flex)
/public (index.php), /src (code), /config (YAML), /var (cache).
Concept 1 : Kernel & Requête
Le "cycle de vie" (Request -> Kernel -> Controller -> Response).
Kernel Request/ResponseConcept 2 : Conteneur (DI)
services.yaml. Injection de Dépendances (DI), autowiring.
Concept 3 : Bundles
Le "plugin" de Symfony. (DoctrineBundle, SecurityBundle...).
Bundles PluginsConcept 4 : Routage
#[Route("/path")] (Attributs/Annotations) ou routes.yaml.
Concept 5 : Contrôleur
AbstractController. render(), json(), redirect().
Concept 6 : Twig (Vues)
Moteur de template (templatetag openvariable ... templatetag closevariable, templatetag openblock ... templatetag endblock). extends, include.
Concept 7 : Doctrine (ORM)
Le "modèle" (DB). Entity, Repository, EntityManager.
Concept 8 : Formulaires
FormBuilder, EntityType, Validation (Assert).
Concept 9 : Sécurité
SecurityBundle. (Authentification, Autorisation, Firewalls).
Addon : API Platform
Wrapper "magique" (Bundle) pour créer des API REST/GraphQL (basé sur Doctrine).
API Platform APIAddon : Messenger & Mercure
Messenger (Queues, Async) & Mercure (Push temps réel).
Cas d'Usage (Vitrine)
Apps "Entreprise", Backends (API), SaaS, (Drupal, Prestashop...).
Entreprise SaaSCheat-sheet
CLI (symfony, composer), bin/console, Workflow (Controller/Route).
Symfony (créé par Fabien Potencier / SensioLabs) est un **Framework PHP** (un ensemble d'outils et de règles) conçu pour créer des applications web robustes et maintenables.
Il est "full-stack" (gère tout, de l'HTTP à la DB) mais aussi "micro" (on peut n'utiliser que les briques (Composants) dont on a besoin).
Diagramme : Symfony (Framework vs Composants)
+------------------------------------------+
| Votre Application (App) |
| (Ex: "E-Shop") |
+------------------------------------------+
| (Utilise)
▼
+------------------------------------------+
| Le Framework Symfony (La "Colle") |
| (Kernel, DI Container, Bundles) |
+------------------------------------------+
| (Utilise)
▼
+------------------------------------------+
| Les Composants Symfony (Les "Briques") |
| (Ex: Console, HttpClient, Routing, |
| Yaml, Serializer, Finder...) |
+------------------------------------------+
Des projets majeurs (comme Drupal, Prestashop, ou Laravel) n'utilisent pas le "Framework" Symfony, mais ils utilisent massivement les "Composants" Symfony.
| Avantage | Description |
|---|---|
| Robuste (Entreprise) | Conçu pour le long terme (LTS), le code "propre" (Tests, DI), et la performance. (Opposé au "code spaghetti" PHP). |
| Flexible (Composants) | Vous pouvez l'utiliser en "micro-framework" (juste le routage) ou "full-stack" (avec Twig, Doctrine, Forms...). |
| Standard (PSR) | Symfony suit (et définit) les standards PSR (PHP Standards Recommendations). (Interopérabilité). |
| Injection de Dépendances | (DI) Le Cœur de Symfony (voir 2.2). Force une architecture découplée (propre) via l'Autowiring. |
| Écosystème (Bundles) | Système de "plugins" (Bundles) très riche (Doctrine, Security, API Platform...). |
symfony CLI & composer)L'installation moderne repose sur 2 outils : Composer (le "npm" de PHP) et Symfony CLI (l'outil d'aide).
1. Les Outils
# 1. Composer (Gestionnaire de paquets PHP) # (Installer globalement, voir getcomposer.org) composer # 2. Symfony CLI (Binaire "Helper") # (Installer globalement, voir symfony.com/download) symfony
2. Création Projet
# 1. Créer un projet (via Symfony CLI) # (Crée un squelette "full-stack" avec --webapp) symfony new mon_projet --webapp # 2. (Alternative: via Composer) composer create-project symfony/skeleton:"^7.0" mon_projet # 3. Installer un "Bundle" (via Symfony Flex) # (Flex (via composer) gère l'installation/config auto) cd mon_projet composer require twig # (Ajoute le moteur de template Twig) composer require maker --dev # (Ajoute les générateurs de code)
3. Lancement (Serveur de Dev)
# (Utiliser le binaire 'symfony' (recommandé)) # (Détecte PHP-FPM, gère Docker, etc.) symfony server:start # (Ouvre http://127.0.0.1:8000)
Symfony Flex (installé par défaut) organise le projet de manière standard (orienté "domaines").
/mon_projet | +-- /bin | +-- console (Le CLI de Symfony: "php bin/console cache:clear") | +-- /config | +-- routes.yaml (Routage (ou via Attributs)) | +-- services.yaml(Conteneur DI (voir 2.2)) | +-- packages/ (Config des Bundles (ex: doctrine.yaml)) | +-- /public | +-- index.php (Le "Front Controller" - Point d'entrée UNIQUE) | +-- (CSS, JS, Images...) | +-- /src | +-- Controller/ (Logique HTTP (Contrôleurs)) | +-- Entity/ (Classes PHP (Doctrine Models)) | +-- Repository/ (Accès DB (Doctrine Repos)) | +-- Form/ (Formulaires) | +-- Kernel.php (Le Cœur de l'app) | +-- /templates | +-- (Fichiers Twig .html.twig) | +-- /var | +-- /cache/ (Cache (compilé)) | +-- /log/ (Logs) | +-- .env (Variables d'env (DATABASE_URL, ...)) +-- composer.json (Dépendances PHP)
Symfony est un framework "Front Controller". **Toutes** les requêtes HTTP (ex: /login, /api/users) arrivent sur **un seul fichier** : /public/index.php.
Diagramme : Cycle de vie (HTTP)
(Navigateur: GET /blog/5)
|
▼
(Serveur Web: Nginx/Apache)
| (Réécrit vers)
▼
+---------------------+
| /public/index.php | (Front Controller)
+---------------------+
| (Crée l'objet "Request")
| (Démarre le "Kernel")
▼
+---------------------+
| Kernel (src/Kernel.php)|
+---------------------+
| (Demande au "Router")
▼
+---------------------+
| Router (Routage) |
| (Trouve: "BlogController::show()")
+---------------------+
| (Appelle)
▼
+---------------------+
| Controller (Votre Code)
| (Ex: "BlogController.php")
| (Retourne un objet "Response")
+---------------------+
|
▼
(Le Kernel envoie la "Response")
|
▼
(Navigateur: Affiche le HTML)
C'est le concept **le plus important** de Symfony (et le plus difficile pour un débutant PHP).
Problème (Sans DI) : Votre Contrôleur a besoin d'un "Mailer" (pour envoyer un email). Vous faites $mailer = new MailerService(). (Votre Contrôleur est "couplé" à MailerService).
Solution (Avec DI) : Vous ne créez (new) **jamais** d'objets (Services). Vous les **demandez** (dans le constructeur). Le **Conteneur de Services** (géré par config/services.yaml) les "injecte" automatiquement (Autowiring).
Exemple (services.yaml & Autowiring)
# (config/services.yaml)
# (Défaut: "autowire: true")
services:
_defaults:
autowire: true
autoconfigure: true
# (Dit à Symfony: "Tous les fichiers dans /src/ sont des 'services'")
App\:
resource: '../src/'
exclude:
- '../src/Kernel.php'
- '../src/Entity/' # (Les Entities ne sont pas des services)
Exemple (Contrôleur)
// (src/Controller/MonController.php)
use Psr\Log\LoggerInterface;
use App\Service\MonSuperService;
class MonController extends AbstractController
{
// 1. (Demande les "services" (objets) dans le constructeur)
public function __construct(
private LoggerInterface $logger,
private MonSuperService $monService
) {
// (PAS DE "new Logger()" !)
}
#[Route('/test')]
public function index(): Response
{
// 2. (Les services sont prêts (injectés) !)
$this->logger->info("Ceci est un log !");
$data = $this->monService->calculerUnTruc();
return $this->render('...);
}
}
Un Bundle est un "plugin" (un dossier) qui ajoute des fonctionnalités (Services, Routes, Config) à Symfony.
L'architecture Symfony Flex (moderne) minimise l'utilisation des Bundles (on préfère l'autowiring simple), mais ils restent essentiels pour les "gros" add-ons.
Bundles Essentiels (Installés via composer require)
| Bundle (Paquet) | Rôle |
|---|---|
symfony/framework-bundle | Le "cœur" (Kernel, DI, Routing, ...). |
symfony/twig-bundle | Ajoute le moteur de template Twig (voir 3.2). |
doctrine/doctrine-bundle | Ajoute l'ORM Doctrine (DB) (voir 3.3). |
symfony/security-bundle | Ajoute la gestion de la sécurité (Login, Firewalls...) (voir 4.1). |
symfony/maker-bundle (Dev) | Ajoute les commandes bin/console make:... (:controller, :entity). |
api-platform/core (API Platform) | Bundle (très complexe) pour créer des API REST/GraphQL (voir 4.2). |
Le "Router" (Routeur) fait le lien entre une URL (Request) et une fonction (Controller).
Méthode moderne (recommandée) : Attributs (PHP 8+) (anciennement "Annotations") directement sur la méthode du contrôleur.
Exemple (src/Controller/BlogController.php)
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; // (PHP 8+)
class BlogController extends AbstractController
{
// 1. Route Simple
#[Route('/blog', name: 'blog_index')]
public function index(): Response
{
// (Logique... ex: $posts = $repo->findAll())
return $this->render('blog/index.html.twig');
}
// 2. Route avec Paramètre (Wildcard)
// (Le 'slug' est injecté dans la variable $slug)
#[Route('/blog/{slug}', name: 'blog_post_show')]
public function show(string $slug): Response
{
// (Logique... ex: $post = $repo->findOneBySlug($slug))
return $this->render('blog/show.html.twig', [
'slug_recu' => $slug,
]);
}
// 3. Route avec Contraintes (Regex)
#[Route('/admin/post/{id}', name: 'admin_post_edit', methods: ['GET', 'POST'], requirements: ['id' => '\d+'])]
public function edit(int $id): Response
{
// (Ne matchera que si 'id' est un nombre (\d+))
// ...
}
}
Le Contrôleur (une classe PHP) est la "colle" (la logique) de la requête.
Rôle : Recevoir la Request (et les services injectés), faire appel au "Modèle" (ex: Doctrine), et retourner une Response.
On hérite de AbstractController pour avoir accès aux raccourcis (render(), json()...).
Exemple (src/Controller/ApiUserController.php)
namespace App\Controller;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; // (L'objet Requête)
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; // (PHP 8+)
class ApiUserController extends AbstractController
{
// (Injection de dépendance (DI))
public function __construct(
private UserRepository $userRepository
) {}
#[Route('/api/users')]
public function getUsers(Request $request): JsonResponse
{
// 1. Lire la requête (ex: paramètre ?limit=10)
$limit = $request->query->get('limit', 10);
// 2. Appeler le "Modèle" (Doctrine)
$users = $this->userRepository->findBy([], [], $limit);
// 3. Retourner une Réponse (JSON)
// (Le raccourci 'json()' gère la sérialisation)
return $this->json([
'users' => $users,
]);
}
#[Route('/page/contact')]
public function contactPage(): Response
{
// 3b. Retourner une Réponse (HTML/Twig)
return $this->render('pages/contact.html.twig', [
'form_status' => 'pending',
]);
}
}
Twig est le moteur de template (moteur de vues) de Symfony. Il sépare la logique (PHP) de l'affichage (HTML).
Syntaxe : {% templatetag openvariable %} ... {% templatetag closevariable %} : Affiche une variable (ex: {% templatetag openvariable %} user.nom {% templatetag closevariable %}). {% templatetag openblock %} ... {% templatetag endblock %} : Exécute une logique (ex: {% templatetag openblock %} if ... {% templatetag endblock %}, {% templatetag openblock %} for ... {% templatetag endblock %}). {# ... #} : Commentaire.
Exemple (templates/blog/show.html.twig)
{# 1. Héritage du layout principal #}
{% extends 'base.html.twig' %}
{# 2. Remplir le "bloc" 'title' (défini dans base.html.twig) #}
{% block title %}Post: {{ post.titre }}{% endblock %}
{# 3. Remplir le "bloc" 'body' #}
{% block body %}
{{ post.titre | upper }}
Par {{ post.auteur.nom }}
{{ post.contenu | raw }} {# | raw = Ne pas échapper le HTML #}
{# 4. Logique (Boucle) #}
Commentaires ({{ post.commentaires | length }})
-
{% for commentaire in post.commentaires %}
- {{ commentaire.texte }} {% else %}
- Aucun commentaire. {% endfor %}
Doctrine est l'ORM (Object-Relational Mapper) par défaut de Symfony. C'est la couche "Modèle" (la base de données).
Il "mappe" (relie) une table SQL (ex: users) à une classe PHP (User).
1. L'Entité (Entity)
Une classe PHP (POPO) dans src/Entity/ qui représente une table. On utilise les Attributs (#[...]) pour définir le mapping.
// (src/Entity/User.php)
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\UserRepository;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: 'users')]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 100, unique: true)]
private ?string $email = null;
#[ORM\Column(length: 255)]
private ?string $nom = null;
// ... (Getters & Setters)
}
Commande (MakerBundle) : php bin/console make:entity User
2. Le Repository (Lire les données)
Classe (dans src/Repository/) utilisée pour **lire** (SELECT) les données. On l'injecte (DI) dans le contrôleur.
// (Controller)
use App\Repository\UserRepository;
public function show(UserRepository $userRepository, int $id): Response
{
// (Méthodes magiques)
$user = $userRepository->find($id);
$user_email = $userRepository->findOneBy(['email' => 'test@test.com']);
$admins = $userRepository->findBy(['role' => 'admin'], ['nom' => 'ASC']);
// (Méthode custom (DQL))
$users_actifs = $userRepository->findActifs();
// ...
}
3. L'EntityManager (Écrire les données)
L'EntityManager ($em) gère l'écriture (INSERT, UPDATE, DELETE).
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\User;
// (Controller)
#[Route('/user/create')]
public function create(EntityManagerInterface $em): Response
{
// 1. Créer l'objet PHP
$user = new User();
$user->setNom("Alice");
$user->setEmail("alice@mail.com");
// 2. "Persist" (Dire à Doctrine de "gérer" cet objet)
$em->persist($user);
// 3. "Flush" (Exécuter le SQL (INSERT) dans la DB)
$em->flush();
return $this->json(['id' => $user->getId()]);
}
Le composant "Form" gère le cycle de vie complet d'un formulaire : création (HTML), "data binding" (liaison Request -> Objet), et validation.
1. Le FormType (Classe)
(php bin/console make:form UserType)
// (src/Form/UserType.php)
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('nom', TextType::class)
// (Si le Form est lié à l'Entité User,
// les types sont auto-détectés !)
;
}
public function configureOptions(OptionsResolver $resolver)
{
// (Lie ce formulaire à l'Entité User)
$resolver->setDefaults(['data_class' => User::class]);
}
}
2. Le Contrôleur (Utilisation)
// (Controller)
#[Route('/user/new')]
public function new(Request $request, EntityManagerInterface $em): Response
{
$user = new User();
// 1. Créer le formulaire
$form = $this->createForm(UserType::class, $user);
// 2. Gérer la requête (POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// (OK: $user a été hydraté)
$em->persist($user);
$em->flush();
return $this->redirectToRoute('homepage');
}
// 3. Afficher (GET)
return $this->render('user/new.html.twig', [
'userForm' => $form->createView()
]);
}
3. La Vue (new.html.twig)
{{ form_start(userForm) }}
{{ form_row(userForm.email) }}
{{ form_row(userForm.nom) }}
{{ form_end(userForm) }}
SecurityBundle)Le SecurityBundle est le composant (très puissant, très complexe) qui gère l'Authentification (AuthN - "Qui es-tu ?") et l'Autorisation (AuthZ - "Qu'as-tu le droit de faire ?").
Tout est configuré dans config/packages/security.yaml.
Diagramme (Flux security.yaml)
(Requête: GET /admin)
|
▼
+---------------------+
| Firewall (le "Mur") | (défini dans 'security.yaml')
| (match: ^/admin) |
+---------------------+
| (STOP: Auth requise)
▼
+---------------------+
| Authenticator | (Ex: Formulaire de login)
| (Vérifie User/Pass) |
+---------------------+
| (OK: User "Bob" (ROLE_ADMIN))
▼
+---------------------+
| Access Control | (Règle: "path: ^/admin, roles: ROLE_ADMIN")
| (Autorisation) |
+---------------------+
| (Accès OK)
▼
(Controller: AdminController)
API Platform (un Bundle/Framework séparé, mais 100% Symfony) est un "wrapper" (enveloppe) "magique" qui lit vos Entités Doctrine (3.3) et génère **automatiquement** une API REST (ou GraphQL) complète (CRUD, filtres, pagination, documentation OpenAPI/Swagger).
Exemple (Magique)
Vous écrivez (presque) *juste* ça :
// (src/Entity/User.php)
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource; // (Importer)
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ApiResource] // (Dire à API Platform d'exposer cette Entité)
class User
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;
#[ORM\Column]
public ?string $nom = null;
// ...
}
Résultat : API Platform crée *automatiquement* les routes, les contrôleurs, et la documentation pour :
GET /api/users(Liste paginée)POST /api/users(Créer)GET /api/users/{id}(Lire)PUT /api/users/{id}(Remplacer)DELETE /api/users/{id}(Supprimer)
Messenger (Asynchrone / Queues)
Le composant "Messenger" gère les **Queues** (files d'attente, ex: RabbitMQ, SQS).
Cas d'usage : Un utilisateur s'inscrit (Requête HTTP). Le Contrôleur (rapide) "dispatch" (envoie) un EmailBienvenueMessage dans la queue, et retourne "OK" (200) à l'utilisateur.
Un "Handler" (worker) (processus séparé, lent) consomme le message et envoie *vraiment* l'email (sans bloquer l'utilisateur).
Mercure (Push Temps Réel)
Mercure (un protocole + un Hub) permet au **serveur** (PHP) de "pousser" (push) des données aux **clients** (JS) en temps réel (Server-Sent Events).
Cas d'usage : Une notification "live", un Tchat.
Symfony (Framework ou Composants) est utilisé par des milliers d'applications "Entreprise" et Open Source.
| Projet | Usage |
|---|---|
| Applications "Entreprise" (SaaS) | Le cas d'usage N°1. (Back-offices, CRM, ERP, SaaS B2B). (Robuste, long terme). |
| API (Backends) | Utilisé comme "micro-framework" ou avec API Platform pour servir des backends (API REST/GraphQL) pour des Frontends (React/Vue). |
| Drupal | (CMS) Drupal (depuis la v8) est entièrement reconstruit sur les Composants Symfony. |
| Prestashop | (E-commerce) Prestashop (depuis la v1.7) utilise les Composants Symfony. |
| Laravel | (Framework PHP) Le concurrent N°1 de Symfony utilise... de nombreux Composants Symfony (ex: Console, HttpClient). |
symfony (Le "Helper")
# 1. Créer un projet (Full-stack) symfony new mon-projet --webapp # 2. Lancer le serveur local symfony server:start # 3. Voir la config (ex: Secrets) symfony var:export --reveal
composer (Les Paquets)
# 1. Installer un Bundle/Composant # (Symfony Flex le configure auto) composer require twig composer require doctrine composer require api # 2. Installer (Dev) composer require maker --dev
php bin/console (Le Cœur Applicatif)
# 1. Lister toutes les commandes php bin/console list # 2. Débugger (Super-utile) php bin/console debug:router php bin/console debug:autowiring # 3. Maker (Générer du code) php bin/console make:controller BlogController php bin/console make:entity User php bin/console make:form UserType php bin/console make:auth # 4. Doctrine (DB) php bin/console doctrine:database:create php bin/console make:migration php bin/console doctrine:migrations:migrate
