Symfony 7.4 LTS
🎯 Objectif : maîtriser Symfony 7.4 LTS pour des projets long-terme, équipes nombreuses, contextes enterprise.
À l'issue de cet axe, tu sauras :
- Comprendre la philosophie Symfony : bundles, services, attributs
- Construire une API REST avec API Platform
- Persister avec Doctrine ORM
- Sécuriser avec security.yaml + LexikJWTAuthenticationBundle
- Mettre en file avec Symfony Messenger
- Tester avec PHPUnit + WebTestCase
Pourquoi Symfony ?
Section intitulée « Pourquoi Symfony ? »| Pour | Contre |
|---|---|
| Modulaire : chaque feature est un bundle, on prend ce qu’on veut | Verbeux par rapport à Laravel |
| Architecture imposée : meilleur sur les gros projets long-terme | Courbe d’apprentissage plus raide |
| API Platform : génère APIs REST + GraphQL automatiquement | Moins de “magie” → plus de boilerplate |
| Composants réutilisables : 60+ packages, utilisés par Laravel et Drupal | Communauté plus restreinte que Laravel |
| Stable : références officielles dans les administrations, banques |
Verdict 2026 : Symfony brille sur les gros projets enterprise (banque, assurance, gouvernement, e-commerce massif). Pour un MVP rapide, Laravel reste plus productif.
Versions 2026
Section intitulée « Versions 2026 »- Symfony 7.4 LTS (novembre 2025) — support 3 ans, choix recommandé.
- Symfony 8.0 (sortie en parallèle) — premier release de la 8.x.
PHP 8.2+ pour 7.4, PHP 8.4+ pour 8.0.
Installation
Section intitulée « Installation »# Symfony CLI (optionnel mais très pratique)curl -sS https://get.symfony.com/cli/installer | bash
# Nouveau projetsymfony new taskly-symfony --version="7.4.*" --webappcd taskly-symfony
# Démarrersymfony server:start# https://localhost:8000 ← HTTPS local automatiqueL’option --webapp ajoute déjà : Twig, Doctrine, Security, Forms, Validator, Messenger, Mailer, etc. Pour une API plus légère, ne mets pas --webapp et installe à la demande.
Structure d’un projet Symfony
Section intitulée « Structure d’un projet Symfony »taskly-symfony/├── bin/console ← CLI Symfony (équivalent artisan)├── config/│ ├── packages/ ← config par bundle│ ├── routes.yaml│ └── services.yaml├── src/│ ├── Controller/│ ├── Entity/ ← Doctrine entities│ ├── Repository/ ← Doctrine repositories│ ├── Form/│ ├── Security/│ └── Kernel.php├── migrations/ ← Doctrine migrations├── templates/ ← Twig├── tests/└── .envRouting par attributs (PHP 8.0+)
Section intitulée « Routing par attributs (PHP 8.0+) »namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\JsonResponse;use Symfony\Component\Routing\Attribute\Route;
#[Route('/api/tasks')]final class TaskController extends AbstractController{ #[Route('', methods: ['GET'])] public function list(): JsonResponse { return $this->json(['data' => []]); }
#[Route('/{id}', methods: ['GET'], requirements: ['id' => '\d+'])] public function show(int $id): JsonResponse { return $this->json(['id' => $id]); }}L’attribut #[Route] remplace la config YAML/XML (encore supportée si tu préfères).
Doctrine ORM
Section intitulée « Doctrine ORM »symfony console make:entity Tasknamespace App\Entity;
use App\Repository\TaskRepository;use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: TaskRepository::class)]class Task{ #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null;
#[ORM\Column(length: 200)] private string $title;
#[ORM\Column(type: 'text', nullable: true)] private ?string $description = null;
#[ORM\Column] private bool $done = false;
#[ORM\Column] private \DateTimeImmutable $createdAt;
#[ORM\ManyToOne(targetEntity: User::class)] #[ORM\JoinColumn(nullable: false)] private User $owner;
public function __construct() { $this->createdAt = new \DateTimeImmutable(); }
// getters et setters générés}symfony console make:migrationsymfony console doctrine:migrations:migrateRepository
Section intitulée « Repository »namespace App\Repository;
use App\Entity\Task;use App\Entity\User;use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;use Doctrine\Persistence\ManagerRegistry;
/** @extends ServiceEntityRepository<Task> */class TaskRepository extends ServiceEntityRepository{ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Task::class); }
/** @return Task[] */ public function findByOwner(User $owner, int $page = 1, int $limit = 20): array { return $this->createQueryBuilder('t') ->andWhere('t.owner = :owner') ->setParameter('owner', $owner) ->orderBy('t.createdAt', 'DESC') ->setFirstResult(($page - 1) * $limit) ->setMaxResults($limit) ->getQuery() ->getResult(); }}API Platform — APIs auto-générées
Section intitulée « API Platform — APIs auto-générées »API Platform est THE killer feature Symfony pour les APIs.
composer require apiuse ApiPlatform\Metadata\ApiResource;use ApiPlatform\Metadata\Get;use ApiPlatform\Metadata\GetCollection;use ApiPlatform\Metadata\Post;use ApiPlatform\Metadata\Patch;use ApiPlatform\Metadata\Delete;
#[ApiResource( operations: [ new GetCollection(), new Get(), new Post(), new Patch(), new Delete(), ], paginationItemsPerPage: 20,)]class Task { /* ... */ }→ 5 endpoints REST + GraphQL + OpenAPI auto générés. Tu obtiens à /api/docs une interface Swagger UI gratuite. Filtres, pagination, sérialisation, validation : tout est déclaratif via attributs.
C’est l’équivalent FastAPI côté PHP.
Validator
Section intitulée « Validator »use Symfony\Component\Validator\Constraints as Assert;
class CreateTaskInput{ #[Assert\NotBlank] #[Assert\Length(min: 1, max: 200)] public string $title;
#[Assert\Length(max: 2000)] public ?string $description = null;}// Dans un contrôleurpublic function create(Request $request, ValidatorInterface $validator): JsonResponse{ $input = $serializer->deserialize($request->getContent(), CreateTaskInput::class, 'json'); $errors = $validator->validate($input); if (count($errors) > 0) { return $this->json(['errors' => (string) $errors], 422); } // ...}API Platform fait ça automatiquement quand tu mets les contraintes sur l’entité.
Sécurité — security.yaml
Section intitulée « Sécurité — security.yaml »security: password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'argon2id'
providers: app_user_provider: entity: class: App\Entity\User property: email
firewalls: api: pattern: ^/api stateless: true jwt: ~ # via LexikJWTAuthenticationBundle
access_control: - { path: ^/api/login, roles: PUBLIC_ACCESS } - { path: ^/api, roles: ROLE_USER }JWT avec LexikJWTAuthenticationBundle
Section intitulée « JWT avec LexikJWTAuthenticationBundle »composer require lexik/jwt-authentication-bundle
# Génération de la paire de clés JWTphp bin/console lexik:jwt:generate-keypairTu obtiens automatiquement les routes /api/login (POST email/password → JWT) et la validation du JWT sur les routes firewalled.
Symfony Messenger — queues
Section intitulée « Symfony Messenger — queues »composer require messengerframework: messenger: transports: async: '%env(MESSENGER_TRANSPORT_DSN)%' # ex. doctrine://default ou redis://localhost routing: App\Message\SendWelcomeEmail: asyncnamespace App\Message;
readonly class SendWelcomeEmail{ public function __construct(public int $userId) {}}
// src/MessageHandler/SendWelcomeEmailHandler.phpnamespace App\MessageHandler;
use App\Message\SendWelcomeEmail;use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]class SendWelcomeEmailHandler{ public function __invoke(SendWelcomeEmail $message): void { // envoyer le mail }}
// Dispatch$bus->dispatch(new SendWelcomeEmail($userId));php bin/console messenger:consume async -vvTests avec WebTestCase
Section intitulée « Tests avec WebTestCase »namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class TaskControllerTest extends WebTestCase{ public function testList(): void { $client = static::createClient(); $client->request('GET', '/api/tasks');
$this->assertResponseStatusCodeSame(401); // sans token }
public function testListAuthenticated(): void { $client = static::createClient(); $user = $this->loginUser($client);
$client->request('GET', '/api/tasks'); $this->assertResponseIsSuccessful(); }}php bin/phpunitAuto-évaluation
Section intitulée « Auto-évaluation »Pour aller plus loin
Section intitulée « Pour aller plus loin »- Symfony Documentation — symfony.com/doc
- SymfonyCasts — symfonycasts.com (vidéos premium)
- API Platform — api-platform.com
- Awesome Symfony — github.com/sl-theory/awesome-symfony
Suite : le projet taskly-api est implémenté en Laravel 12 (référence). Pour faire l’équivalent en Symfony, suis la doc API Platform avec les entités ci-dessus.