Symfony Entity Tutoriel : Comment Créer sa Première Entité
Blog, Développement, Tutoriels

Symfony Entity Tutoriel : Comment Créer sa Première Entité

Vous voulez gérer des données dans votre projet Symfony sans vous prendre la tête ? Comment créer un lien simple et solide entre votre code et votre base de données ? Vous cherchez un guide clair pour maîtriser les Entités Doctrine ?

Ce tutoriel vous guide pas à pas pour créer votre première entité Symfony avec Doctrine, de la configuration initiale à son utilisation dans un contrôleur. Pas de blabla, juste du concret. Pour la théorie, vous pouvez toujours consulter l’ORM Doctrine et la documentation officielle de Symfony sur Doctrine.

Les prérequis indispensables avant de commencer

Avant d’écrire la moindre ligne de code, assurez-vous d’avoir tout ce qu’il faut. C’est simple et ça évite les erreurs bêtes plus tard.

Voici ce dont vous avez besoin :

  • Un projet Symfony 7 (ou plus récent) déjà installé sur votre machine.
  • Composer, le gestionnaire de paquets pour PHP, doit être fonctionnel.
  • Un accès à un terminal ou une ligne de commande.
  • Une base de données qui tourne (MySQL, MariaDB, PostgreSQL, etc.).

Si vous avez tout ça, vous êtes prêt à continuer.

Étape 1 : Installer Doctrine et configurer la base de données

La première étape est de s’assurer que Symfony peut parler à votre base de données. Pour ça, on a besoin de Doctrine, l’ORM (Object-Relational Mapper) par défaut de Symfony. C’est lui qui fait le pont entre vos objets PHP et les tables de votre BDD.

Ouvrez votre terminal et tapez ces deux commandes pour installer les paquets nécessaires. Le premier, `orm-pack`, contient tout ce qu’il faut pour Doctrine. Le second, `maker-bundle`, nous donnera accès à des commandes pour générer du code facilement.

composer require symfony/orm-pack
composer require --dev symfony/maker-bundle

Maintenant, il faut dire à Symfony où se trouve votre base de données. Tout se passe dans le fichier `.env` à la racine de votre projet. Cherchez la ligne qui commence par `DATABASE_URL`.

Cette ligne a un format précis. Elle indique le type de base de données, l’utilisateur, le mot de passe, l’adresse, le port et le nom de la base. Par exemple : `driver://user:password@host:port/dbname`.

Exemples de configuration pour votre DATABASE_URL

Voici comment adapter la ligne pour les systèmes les plus courants. Remplacez les valeurs par les vôtres.

Pour MySQL / MariaDB :

# Dans votre fichier .env
DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=10.11.2-mariadb&charset=utf8mb4"

Pour PostgreSQL :

# Dans votre fichier .env
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=16&charset=utf8"

Une fois le fichier `.env` enregistré, vous pouvez demander à Doctrine de créer la base de données si elle n’existe pas encore. C’est une commande pratique pour partir de zéro.

php bin/console doctrine:database:create

Si tout se passe bien, la console affichera un message de succès. Votre projet est maintenant connecté à la base de données.

Étape 2 : Créer sa première Entité avec `make:entity`

C’est le moment de créer notre première entité. Une entité, c’est juste une classe PHP qui représente une table dans votre base de données. Chaque propriété de la classe correspondra à une colonne de la table.

Grâce au `maker-bundle`, on a une commande magique pour ça : `make:entity`. Lancez-la dans votre terminal.

php bin/console make:entity

La console va vous poser une série de questions. C’est un assistant qui va générer le fichier pour vous. Suivez simplement les étapes.

Le processus interactif

Voici à quoi ressemble une session typique pour créer une entité `Article` avec un titre, un contenu et une date de création.

 Class name of the entity to create or update (e.g. BravePuppy):
 > Article

 New property name (press <return> to stop adding fields):
 > title

 Field type (enter ? to see all types) [string]:
 > string

 Field length [255]:
 > 255

 Can this field be null in the database? (yes/no) [no]:
 > no

 New property name (press <return> to stop adding fields):
 > content

 Field type (enter ? to see all types) [string]:
 > text

 Can this field be null in the database? (yes/no) [no]:
 > no

 New property name (press <return> to stop adding fields):
 > createdAt

 Field type (enter ? to see all types) [string]:
 > datetime

 Can this field be null in the database? (yes/no) [no]:
 > no

 New property name (press <return> to stop adding fields):
 > 

 Success! 

 Next: When you're ready, create a migration with php bin/console make:migration

Et voilà. Symfony vient de créer deux fichiers pour vous : `src/Entity/Article.php` et `src/Repository/ArticleRepository.php`. On va voir juste après à quoi ils servent.

Anatomie des fichiers générés : `Article.php` et `ArticleRepository.php`

La commande `make:entity` a généré du code, mais qu’est-ce que ça veut dire ? C’est important de comprendre le rôle de chaque fichier pour bien utiliser Doctrine.

Le fichier Entité (`src/Entity/Article.php`)

Le fichier d’Entité est le cœur du système. Il s’agit d’une simple classe PHP qui va contenir les données d’un article. Chaque instance de cette classe représentera une ligne dans la table `article`.

Le lien avec la base de données se fait grâce à des attributs PHP (les `#[ORM\…]`). C’est la méthode moderne utilisée par Symfony 7. Ils disent à Doctrine comment mapper les propriétés de la classe aux colonnes de la table.

<?php

namespace App\Entity;

use App\Repository\ArticleRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

// Cet attribut dit à Doctrine que cette classe est une entité
// et qu'elle est gérée par ArticleRepository.
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article
{
    // #[ORM\Id] définit la clé primaire.
    // #[ORM\GeneratedValue] indique que la BDD génère la valeur (auto-increment).
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    // #[ORM\Column] mappe cette propriété à une colonne de la table.
    // Le type est VARCHAR(255) par défaut.
    #[ORM\Column(length: 255)]
    private ?string $title = null;

    // Le type 'text' correspond à une colonne TEXT en SQL, pour les contenus longs.
    #[ORM\Column(type: Types::TEXT)]
    private ?string $content = null;

    // Le type 'datetime' correspond à une colonne DATETIME en SQL.
    #[ORM\Column(type: Types::DATETIME_MUTABLE)]
    private ?\DateTimeInterface $createdAt = null;

    // ... getters et setters générés automatiquement ...

    public function getId(): ?int
    {
        return $this->id;
    }

    // ... autres getters/setters
}

Le fichier Repository (`src/Repository/ArticleRepository.php`)

Le fichier de Repository a un rôle différent. Sa mission est de vous permettre de récupérer des entités depuis la base de données. C’est ici que vous écrirez vos requêtes SQL (ou plutôt DQL, le langage de Doctrine).

Par défaut, il contient déjà des méthodes utiles comme `findAll()` pour tout récupérer, ou `findBy()` pour chercher avec des critères. C’est la classe que vous utiliserez le plus souvent dans vos contrôleurs pour lire des données.

<?php

namespace App\Repository;

use App\Entity\Article;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @extends ServiceEntityRepository<Article>
 */
class ArticleRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Article::class);
    }

    // Exemple de méthode générée (peut être enlevée si non utilisée)
    //    /**
    //     * @return Article[] Returns an array of Article objects
    //     */
    //    public function findByExampleField($value): array
    //    {
    //        return $this->createQueryBuilder('a')
    //            ->andWhere('a.exampleField = :val')
    //            ->setParameter('val', $value)
    //            ->orderBy('a.id', 'ASC')
    //            ->setMaxResults(10)
    //            ->getQuery()
    //            ->getResult()
    //        ;
    //    }
}

Étape 3 : Appliquer la structure à la base de données avec les Migrations

Pour l’instant, on a défini notre structure en PHP, mais la base de données est toujours vide. Il faut maintenant créer la table `article` correspondante. C’est le rôle des migrations.

Une migration est un fichier qui contient les requêtes SQL nécessaires pour mettre à jour la base de données. C’est une sorte de versioning pour la structure de votre BDD. Ça permet de garder une trace de toutes les modifications et de les appliquer de manière fiable.

La première commande, `make:migration`, va comparer votre code (vos entités) avec l’état actuel de la base de données et générer un fichier de migration avec les différences.

php bin/console make:migration

Cette commande va créer un nouveau fichier dans le dossier `migrations/`. Ce fichier contient le SQL pour créer la table `article` avec les bonnes colonnes (`id`, `title`, `content`, `created_at`).

La deuxième commande, `doctrine:migrations:migrate`, exécute toutes les nouvelles migrations qui n’ont pas encore été appliquées.

php bin/console doctrine:migrations:migrate

La console vous demandera une confirmation. Répondez `yes` et la commande exécutera le SQL. Vous pouvez maintenant ouvrir votre outil de gestion de base de données (comme phpMyAdmin ou DBeaver) et vérifier : la table `article` a bien été créée !

Étape 4 : Manipuler et afficher les Entités dans un Contrôleur

Maintenant que tout est en place, il est temps d’utiliser notre entité. On va créer un contrôleur pour afficher des articles et en créer de nouveaux.

Utilisons à nouveau le `maker-bundle` pour créer un contrôleur rapidement.

php bin/console make:controller ArticleController

Ouvrez le fichier `src/Controller/ArticleController.php`. On va modifier la méthode `index()` pour récupérer et afficher des articles.

Pour interagir avec les données, on a besoin de deux services principaux :

  • L’`ArticleRepository` : pour lire les données (ex: trouver tous les articles).
  • L’`EntityManagerInterface` : pour écrire des données (créer, modifier, supprimer).

Grâce à l’injection de dépendances de Symfony, il suffit de les demander en argument de notre méthode, et Symfony s’occupe de nous les fournir.

Exemple de code pour le contrôleur et la vue

Voici un exemple complet qui crée un article s’il n’y en a pas, puis affiche la liste de tous les articles.

Dans `src/Controller/ArticleController.php` :

<?php

namespace App\Controller;

use App\Entity\Article;
use App\Repository\ArticleRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ArticleController extends AbstractController
{
    #[Route('/articles', name: 'app_article_index')]
    public function index(ArticleRepository $articleRepository, EntityManagerInterface $entityManager): Response
    {
        // 1. On récupère tous les articles avec findAll()
        $articles = $articleRepository->findAll();

        // Si la table est vide, on crée un article de démo
        if (empty($articles)) {
            $demoArticle = new Article();
            $demoArticle->setTitle('Mon premier article');
            $demoArticle->setContent('Le contenu de mon tout premier article.');
            $demoArticle->setCreatedAt(new \DateTime());

            // 2. On dit à Doctrine de "surveiller" cet objet
            $entityManager->persist($demoArticle);
            
            // 3. On exécute la requête INSERT en base de données
            $entityManager->flush();

            // On rafraîchit la liste pour inclure le nouvel article
            $articles = $articleRepository->findAll();
        }

        // 4. On passe la liste des articles à la vue Twig
        return $this->render('article/index.html.twig', [
            'articles' => $articles,
        ]);
    }
}

Dans `templates/article/index.html.twig` :

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

{% block title %}Liste des Articles{% endblock %}

{% block body %}
    <h1>Tous nos articles</h1>

    {% if articles is not empty %}
        <ul>
        {% for article in articles %}
            <li>
                <h2>{{ article.title }}</h2>
                <p>{{ article.content }}</p>
                <small>Publié le {{ article.createdAt|date('d/m/Y') }}</small>
            </li>
        {% endfor %}
        </ul>
    {% else %}
        <p>Aucun article trouvé.</p>
    {% endif %}
{% endblock %}

Rendez-vous sur l’URL `/articles` de votre projet. La première fois, le code va créer un article, l’enregistrer, puis l’afficher. Si vous rafraîchissez la page, il ne créera plus l’article mais affichera directement celui qui existe déjà.

FAQ – Questions fréquentes sur les Entités Symfony

Voici quelques questions que les développeurs se posent souvent quand ils débutent avec les entités Doctrine.

Comment ajouter, modifier ou supprimer un champ d’une entité existante ?
C’est un processus en trois temps. D’abord, modifiez le fichier de votre entité PHP (par exemple, ajoutez une nouvelle propriété `$author`). Ensuite, lancez la commande `php bin/console make:migration` pour que Doctrine génère le SQL correspondant (un `ALTER TABLE`). Enfin, appliquez la modification à votre base de données avec `php bin/console doctrine:migrations:migrate`.

Quelle est la différence entre une Entité et un Repository ?
C’est simple : l’Entité représente une seule ligne de la table (un objet avec des données). Le Repository représente la table entière et sert à faire des requêtes pour trouver une ou plusieurs lignes (des entités).

Peut-on créer une entité sans la lier à la base de données ?
Oui. Une entité est avant tout une classe PHP. Si vous retirez l’attribut `#[ORM\Entity]` au-dessus de la déclaration de la classe, Doctrine ne la gérera plus. Elle deviendra un simple « POPO » (Plain Old PHP Object) que vous pourrez utiliser pour autre chose dans votre application.

Vous pourriez également aimer...