Un blog, c’est d’abord une entité et un moyen de la lire. Cet épisode pose le système d’articles côté Symfony : la classe, sa table, son repository et les pages qui les affichent.
La série a été enregistrée sur Symfony 6. Les principes n’ont pas bougé, mais la syntaxe a changé sur un point de fond : les annotations n’existent plus côté Symfony. Ce qui suit est la version à jour.
L’entité Article
make:entity génère la classe et ses attributs. Le résultat ressemble à ceci :
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $content = null;
}
Le préfixe use Doctrine\ORM\Mapping as ORM; permet d’écrire #[ORM\Column] au lieu du chemin complet. Ces attributs sont natifs à PHP 8 : ce sont les successeurs directs des annotations, et c’est la seule syntaxe acceptée depuis Symfony 7.0.
Pour un article de blog, deux champs méritent un soin particulier : le slug, qui sert dans l’URL et doit être unique, et publishedAt, un DateTimeImmutable qui permet de trier et de ne pas afficher les brouillons.
Créer la table
L’entité décrit la table, la migration la crée :
php bin/console make:migration
php bin/console doctrine:migrations:migrate
La première commande compare ton mapping à l’état réel de la base et écrit un fichier de migration ; la seconde l’applique. C’est le chemin à suivre, y compris en développement : doctrine:schema:update modifie la structure sans laisser de trace, ce qui devient ingérable dès qu’il y a une base à ne pas casser.
Si la base n’existe pas encore, doctrine:database:create la crée à partir de DATABASE_URL.
Le repository
Le repository généré étend ServiceEntityRepository et reçoit le registre des gestionnaires d’entités :
class ArticleRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Article::class);
}
}
C’est le lien entre ton entité et Doctrine : la classe est déclarée sur l’entité via repositoryClass, donc getRepository(Article::class) renvoie bien cette classe-là. Tu y ajoutes tes requêtes métier, en DQL quand l’expression le justifie :
$query = $this->getEntityManager()
->createQuery('SELECT a FROM App\Entity\Article a WHERE a.publishedAt IS NOT NULL ORDER BY a.publishedAt DESC');
Pour une condition simple, findBy() et findOneBy() suffisent et évitent d’écrire du DQL.
Persister un article
L’écriture passe par l’EntityManagerInterface, injecté dans le contrôleur :
$entityManager->persist($article);
$entityManager->flush();
La distinction compte : persist() programme l’objet, flush() exécute réellement les requêtes. Tant que tu n’as pas appelé flush(), rien n’est écrit en base.
Afficher les articles
Les contrôleurs se génèrent avec make:controller. Pour un blog, le motif habituel consiste à regrouper les routes sous un préfixe au niveau de la classe :
#[Route('/blog', name: 'blog_')]
class BlogController extends AbstractController
{
#[Route('/{slug}', name: 'show')]
public function show(Article $article): Response
Avec MapEntity sur le paramètre, Symfony convertit le slug en entité sans que tu écrives la requête, et renvoie un 404 si l’article n’existe pas. Une seule déclaration remplace trois lignes de recherche manuelle et le contrôle de nullité qui va avec.
Dans le template, path('blog_show', { slug: article.slug }) génère l’URL à partir du nom de route plutôt que de la concaténer à la main.
Ce qui change par rapport à la série
- Les annotations deviennent des attributs :
@ORM\Columns’écrit#[ORM\Column]. Le support des annotations a été retiré en Symfony 7.0, ce n’est pas une simple dépréciation. - Plus de
$this->getDoctrine()dans les contrôleurs : injecteEntityManagerInterfaceou directement ton repository. - Les versions : un projet neuf partira sur Symfony 8.1 (PHP 8.4) ou sur la LTS 7.4, pas sur 6.x.
L’épisode suivant de la série, le template de base avec Bootstrap 5, montre comment ces articles sont rendus. La documentation Doctrine de Symfony détaille les types de champs et les associations, et la documentation du routage la conversion des paramètres.



