1. Installation et configuration
Symfony 7 s'installe via Composer et la Symfony CLI, un outil en ligne de commande qui simplifie le développement local. Avant de commencer, assurez-vous de disposer de PHP 8.2 minimum (PHP 8.3 ou 8.4 recommandé), Composer 2.x et Git.
Installer la Symfony CLI
La Symfony CLI offre un serveur de développement local avec support HTTPS, la détection automatique des problèmes de configuration et l'intégration avec les services Docker. Installez-la selon votre système d'exploitation :
# Linux / macOS
curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | sudo -E bash
sudo apt install symfony-cli
# Windows (via Scoop)
scoop install symfony-cli
# Vérifier l'installation
symfony check:requirements
Créer un nouveau projet
Symfony propose deux squelettes de départ. Le squelette webapp inclut tout le nécessaire pour une application web complète (Twig, Doctrine, formulaires, sécurité). Le squelette minimal est idéal pour les API ou les microservices.
# Application web complète
symfony new mon-projet --webapp
# API ou microservice
symfony new mon-api
# Avec une version spécifique de Symfony
symfony new mon-projet --version=7.2 --webapp
Symfony Flex, le plugin Composer intégré, automatise l'installation et la configuration de chaque bundle via un système de recettes. Quand vous ajoutez un package, Flex crée les fichiers de configuration, les variables d'environnement et les dossiers nécessaires. C'est un gain de temps considérable par rapport à la configuration manuelle.
Structure d'un projet Symfony 7
La structure d'un projet Symfony 7 est claire et prévisible :
mon-projet/
├── bin/console # Point d'entrée CLI
├── config/ # Configuration (services, routes, packages)
│ ├── packages/ # Configuration par package
│ └── routes.yaml # Définition des routes
├── migrations/ # Migrations Doctrine
├── public/ # Racine web (index.php)
├── src/
│ ├── Controller/ # Contrôleurs
│ ├── Entity/ # Entités Doctrine
│ ├── Repository/ # Repositories Doctrine
│ └── Kernel.php # Noyau de l'application
├── templates/ # Templates Twig
├── tests/ # Tests PHPUnit
├── var/ # Cache et logs
├── vendor/ # Dépendances Composer
├── .env # Variables d'environnement
└── composer.json
2. Architecture MVC de Symfony 7
Symfony implémente le pattern MVC (Model-View-Controller) de manière rigoureuse, avec une séparation nette des responsabilités. Pour approfondir les raisons qui font de cette architecture un atout majeur, consultez notre article sur pourquoi utiliser Symfony.
Les contrôleurs
Un contrôleur Symfony 7 est une classe PHP qui définit des actions associées à des routes. Depuis Symfony 6.2, les attributs PHP 8 remplacent les annotations Doctrine pour déclarer les routes directement sur les méthodes :
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/articles', name: 'article_list', methods: ['GET'])]
public function list(ArticleRepository $repository): Response
{
$articles = $repository->findAllPublished();
return $this->render('article/list.html.twig', [
'articles' => $articles,
]);
}
#[Route('/articles/{slug}', name: 'article_show', methods: ['GET'])]
public function show(Article $article): Response
{
return $this->render('article/show.html.twig', [
'article' => $article,
]);
}
}
Le ParamConverter (via l'attribut #[MapEntity] ou la résolution automatique) convertit les paramètres de route en entités Doctrine. Dans l'exemple ci-dessus, Symfony récupère automatiquement l'article correspondant au slug passé dans l'URL.
L'injection de dépendances
Le conteneur de services est le cœur de Symfony. Grâce à l'autowiring, les dépendances sont résolues automatiquement à partir des type-hints PHP. Vous n'avez presque jamais besoin de configurer manuellement les services :
class NewsletterService
{
public function __construct(
private readonly MailerInterface $mailer,
private readonly UserRepository $userRepository,
private readonly LoggerInterface $logger,
) {}
public function sendWeeklyDigest(): void
{
$subscribers = $this->userRepository->findSubscribers();
// ...
}
}
En production, le conteneur est compilé en une classe PHP optimisée. Toutes les résolutions sont précalculées, ce qui élimine le coût d'exécution du conteneur et offre un avantage de performance unique dans l'écosystème PHP.
Le système d'événements
Symfony utilise un EventDispatcher qui permet de découpler les composants de l'application. Les listeners et subscribers réagissent à des événements sans créer de dépendance directe entre les classes. Ce pattern est fondamental pour construire des applications extensibles et maintenables.
3. Doctrine ORM : la persistance maîtrisée
Doctrine ORM est l'outil de persistance de référence dans Symfony. Il implémente le pattern Data Mapper, ce qui signifie que les entités sont de simples objets PHP (POPO) sans connaissance de la base de données. La couche de persistance est entièrement gérée par l'EntityManager.
Définir une entité
Les entités utilisent des attributs PHP 8 pour le mapping objet-relationnel :
namespace App\Entity;
use App\Repository\ArticleRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
#[ORM\Table(name: 'articles')]
#[ORM\HasLifecycleCallbacks]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $title;
#[ORM\Column(type: Types::TEXT)]
private string $content;
#[ORM\Column(length: 255, unique: true)]
private string $slug;
#[ORM\Column]
private \DateTimeImmutable $createdAt;
#[ORM\ManyToOne(targetEntity: Category::class, inversedBy: 'articles')]
#[ORM\JoinColumn(nullable: false)]
private Category $category;
#[ORM\PrePersist]
public function setCreatedAtValue(): void
{
$this->createdAt = new \DateTimeImmutable();
}
// Getters et setters...
}
Migrations
Les migrations Doctrine versionnent les changements de schéma de base de données. Chaque migration est une classe PHP avec des méthodes up() et down() qui contiennent les instructions SQL :
# Générer une migration à partir des différences
php bin/console doctrine:migrations:diff
# Exécuter les migrations
php bin/console doctrine:migrations:migrate
# Vérifier le statut
php bin/console doctrine:migrations:status
QueryBuilder et DQL
Pour les requêtes complexes, Doctrine propose le QueryBuilder et le DQL (Doctrine Query Language), un langage de requête orienté objet similaire à SQL mais opérant sur les entités plutôt que sur les tables :
class ArticleRepository extends ServiceEntityRepository
{
public function findPublishedByCategory(Category $category, int $limit = 10): array
{
return $this->createQueryBuilder('a')
->andWhere('a.category = :category')
->andWhere('a.publishedAt IS NOT NULL')
->andWhere('a.publishedAt <= :now')
->setParameter('category', $category)
->setParameter('now', new \DateTimeImmutable())
->orderBy('a.publishedAt', 'DESC')
->setMaxResults($limit)
->getQuery()
->getResult();
}
}
4. Twig : le moteur de templates
Twig est le moteur de templates de Symfony, développé par SensioLabs. Il impose une séparation stricte entre la logique et la présentation : il est impossible d'écrire du PHP dans un template Twig. Cette contrainte délibérée produit des templates propres et maintenables.
Syntaxe de base
Twig utilise trois types de délimiteurs : {{ }} pour afficher des variables, {% %} pour les instructions de contrôle, et {# #} pour les commentaires :
{# templates/article/show.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}{{ article.title }} | Code Your Web{% endblock %}
{% block body %}
<article>
<h1>{{ article.title }}</h1>
<time datetime="{{ article.createdAt|date('Y-m-d') }}">
{{ article.createdAt|date('d/m/Y') }}
</time>
<div class="content">
{{ article.content|raw }}
</div>
{% if article.tags is not empty %}
<ul class="tags">
{% for tag in article.tags %}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>
{% endif %}
</article>
{% endblock %}
Héritage de templates
Le système d'héritage de Twig est l'un de ses points forts. Un template de base définit la structure HTML globale avec des blocs que les templates enfants peuvent surcharger. C'est comparable à l'héritage de classes en programmation orientée objet, appliqué aux templates.
Composants Twig et Symfony UX
Symfony 7 intègre TwigComponent et LiveComponent, qui permettent de créer des composants réutilisables avec un comportement interactif côté serveur. Couplés à Turbo (de Hotwire), ils offrent une alternative légère aux frameworks JavaScript pour les interfaces dynamiques. Pour comprendre comment Symfony s'est imposé dans cet écosystème, lisez notre article sur l'explosion de Symfony.
5. API Platform : créer des API modernes
API Platform est la solution de référence pour construire des API REST et GraphQL avec Symfony. Basé sur les entités Doctrine, il génère automatiquement une API complète avec documentation OpenAPI, pagination, filtres, validation et sérialisation.
Exposer une entité en API
Il suffit d'ajouter l'attribut #[ApiResource] sur une entité pour générer automatiquement les endpoints CRUD :
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
#[ApiResource(
operations: [
new GetCollection(),
new Get(),
new Post(security: "is_granted('ROLE_ADMIN')"),
],
paginationItemsPerPage: 20,
)]
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article
{
// ...
}
API Platform génère automatiquement la documentation Swagger/OpenAPI, les formats JSON-LD et HAL, la pagination avec curseurs, les filtres de recherche et le tri. C'est un accélérateur considérable pour les projets qui nécessitent une API robuste.
6. Sécurité et authentification
Le composant Security de Symfony 7 couvre tous les aspects de la sécurité web : authentification, autorisation, gestion des utilisateurs et protection contre les attaques courantes.
Authentification
Symfony 7 simplifie l'authentification avec le système de firewalls et d'authenticators. Le LoginFormAuthenticator gère l'authentification par formulaire, tandis que les AccessToken authenticators prennent en charge l'authentification par jetons pour les API :
# config/packages/security.yaml
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
main:
lazy: true
provider: app_user_provider
form_login:
login_path: app_login
check_path: app_login
logout:
path: app_logout
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
Voters : permissions granulaires
Les Voters permettent de définir des règles d'autorisation complexes et réutilisables. Contrairement aux simples rôles, un Voter peut vérifier si un utilisateur a le droit de modifier un article précis, en tenant compte du contexte (auteur, statut, date, etc.).
7. Tests automatisés
Symfony 7 fournit un écosystème de tests complet, de l'unité au test end-to-end en navigateur réel.
Tests unitaires
Les tests unitaires vérifient le comportement isolé d'une classe ou d'une méthode :
use PHPUnit\Framework\TestCase;
class SluggerTest extends TestCase
{
public function testSlugify(): void
{
$slugger = new Slugger();
$this->assertSame('mon-article', $slugger->slugify('Mon Article'));
$this->assertSame('caf-cr-me', $slugger->slugify('Café Crème'));
}
}
Tests fonctionnels
Les tests fonctionnels simulent des requêtes HTTP et vérifient les réponses de l'application. Le composant BrowserKit fournit un client HTTP intégré qui permet de tester les contrôleurs sans serveur web :
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ArticleControllerTest extends WebTestCase
{
public function testArticlePageReturns200(): void
{
$client = static::createClient();
$client->request('GET', '/articles');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('h1', 'Articles');
}
}
Tests end-to-end avec Panther
Symfony Panther exécute les tests dans un vrai navigateur Chrome (headless), ce qui permet de vérifier le JavaScript, les animations CSS et les interactions utilisateur réelles. C'est l'outil idéal pour tester les composants Symfony UX, les formulaires dynamiques et les parcours utilisateur complets.
8. Déploiement en production
Le déploiement d'une application Symfony 7 en production implique plusieurs étapes critiques pour garantir performance et sécurité.
Préparation de la production
# Compiler l'environnement de production
composer install --no-dev --optimize-autoloader
# Compiler le conteneur et vider le cache
APP_ENV=prod APP_DEBUG=0 php bin/console cache:clear
APP_ENV=prod php bin/console cache:warmup
# Exécuter les migrations
php bin/console doctrine:migrations:migrate --no-interaction
# Compiler les assets (AssetMapper)
php bin/console asset-map:compile
Configuration serveur
En production, Symfony 7 fonctionne de manière optimale avec Nginx ou Caddy comme serveur web, couplé à PHP-FPM. L'activation d'OPcache et du preloading PHP 8 réduit considérablement les temps de réponse. Pour les applications à fort trafic, un reverse proxy Varnish avec support ESI permet de cacher les pages et les fragments.
Automatisation avec Deployer
Deployer est l'outil de déploiement le plus utilisé avec Symfony. Il gère les releases atomiques (chaque déploiement est une release isolée avec possibilité de rollback), l'exécution des migrations et la gestion du cache. Les pipelines CI/CD (GitHub Actions, GitLab CI, Bitbucket Pipelines) automatisent la chaîne complète : tests, analyse statique, build et déploiement.
Monitoring
En production, le Symfony Profiler est désactivé, mais le composant Monolog centralise les logs. L'intégration avec des outils comme Sentry, Datadog ou New Relic permet de surveiller les erreurs, les performances et l'utilisation des ressources. Le composant Messenger fournit des métriques sur le traitement des messages asynchrones.
Si vous hésitez entre les principaux frameworks PHP, notre comparaison détaillée entre Symfony, Laravel et Yii2 vous aidera à faire un choix éclairé selon vos besoins spécifiques.