1 / 20

Les entités et leurs relations dans Symfony 6.4

🚀

Guide pour les débutants

Au programme 📚

Qu'est-ce qu'une entité ? 🤔

Une entité est une classe PHP qui représente une table dans votre base de données.

Imaginez une entité comme un formulaire papier :

  • Le formulaire = l'entité (la classe)
  • Les champs = les propriétés
  • Chaque formulaire rempli = un enregistrement

Symfony utilise Doctrine ORM pour gérer automatiquement les interactions avec la base de données.

Structure d'une entité 🏗️

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Author
{
    #[ORM\Id]                    // ← Clé primaire
    #[ORM\GeneratedValue]        // ← Auto-incrément
    #[ORM\Column]               // ← Colonne en base
    private ?int $id = null;

    #[ORM\Column(length: 255)]  // ← VARCHAR(255)
    private ?string $name = null;
    
    // Getters et setters...
}

Créer votre première entité 🎯

Étape 1 : Générer l'entité

php bin/console make:entity

Étape 2 : Répondre aux questions

Étape 3 : Créer la migration

php bin/console make:migration php bin/console doctrine:migrations:migrate

Les types de relations 🔗

OneToMany / ManyToOne

Un auteur écrit plusieurs livres
Un livre a un seul auteur

ManyToMany

Un étudiant suit plusieurs cours
Un cours a plusieurs étudiants

OneToOne

Une personne a un passeport
Un passeport appartient à une personne

Règle importante ⚡

Dans une relation OneToMany / ManyToOne :

OneToMany = côté "un"
L'auteur a ses livres

ManyToOne = côté "plusieurs"
Le livre a son auteur

Entité Author (côté OneToMany) 👤

#[ORM\Entity]
class Author
{
    // Propriétés de base...
    
    #[ORM\OneToMany(
        mappedBy: 'author',           // ← Propriété dans Book
        targetEntity: Book::class,    // ← Classe cible
        cascade: ['persist', 'remove'] // ← Actions auto
    )]
    private Collection $books;

    public function __construct()
    {
        // 🚨 OBLIGATOIRE : Initialiser la collection
        $this->books = new ArrayCollection();
    }
}

Méthodes pour gérer la collection 🔧

public function addBook(Book $book): static
{
    if (!$this->books->contains($book)) {
        $this->books->add($book);
        $book->setAuthor($this); // 🚨 Maintenir la cohérence
    }
    return $this;
}

public function removeBook(Book $book): static
{
    if ($this->books->removeElement($book)) {
        if ($book->getAuthor() === $this) {
            $book->setAuthor(null);
        }
    }
    return $this;
}

Entité Book (côté ManyToOne) 📚

#[ORM\Entity]
class Book
{
    // Propriétés de base...
    
    #[ORM\ManyToOne(
        inversedBy: 'books'    // ← Propriété dans Author
    )]
    #[ORM\JoinColumn(nullable: false)] // ← Un livre DOIT avoir un auteur
    private ?Author $author = null;

    public function getAuthor(): ?Author
    {
        return $this->author;
    }

    public function setAuthor(?Author $author): static
    {
        $this->author = $author;
        return $this;
    }
}

Comprendre les annotations 📝

ManyToMany : Student 🎓

#[ORM\Entity]
class Student
{
    #[ORM\ManyToMany(
        targetEntity: Course::class, 
        inversedBy: 'students'
    )]
    #[ORM\JoinTable(name: 'student_course')] // ← Table intermédiaire
    private Collection $courses;

    public function __construct()
    {
        $this->courses = new ArrayCollection();
    }
    
    // Méthodes add/remove similaires...
}

ManyToMany : Course 📖

#[ORM\Entity]
class Course
{
    #[ORM\ManyToMany(
        targetEntity: Student::class, 
        mappedBy: 'courses'  // ← Propriété dans Student
    )]
    private Collection $students;

    public function __construct()
    {
        $this->students = new ArrayCollection();
    }
    
    // Méthodes add/remove...
}

Une table intermédiaire student_course sera créée automatiquement !

Erreur fréquente #1 ❌

Collection non initialisée

class Author
{
    private Collection $books; // Pas initialisé !
    
    public function addBook(Book $book): static
    {
        $this->books->add($book); // ← ERREUR : null->add()
    }
}

Toujours initialiser dans le constructeur :

public function __construct()
{
    $this->books = new ArrayCollection();
}

Erreur fréquente #2 ❌

Relation non synchronisée

public function addBook(Book $book): static
{
    $this->books->add($book);
    // Oubli de : $book->setAuthor($this);
    return $this;
}

Toujours maintenir la cohérence des deux côtés :

public function addBook(Book $book): static
{
    if (!$this->books->contains($book)) {
        $this->books->add($book);
        $book->setAuthor($this); // ← Maintenir la cohérence
    }
    return $this;
}

Tester vos relations 🧪

#[Route('/test-entities')]
public function testEntities(EntityManagerInterface $em): Response
{
    // Créer un article
    $article = new Article();
    $article->setTitle('Mon premier article');
    
    // Créer un commentaire
    $comment = new Comment();
    $comment->setContent('Super article !');
    $comment->setAuthor('Alice');
    
    // Lier le commentaire à l'article
    $article->addComment($comment);
    
    // Sauvegarder (cascade fait le reste)
    $em->persist($article);
    $em->flush();
    
    return new Response('Succès !');
}

Checklist ✅

Avant de créer une relation

  • Les deux entités existent ?
  • Quel type de relation ?
  • Qui est le propriétaire ?

Lors de l'implémentation

  • Collection initialisée ?
  • Annotations correctes ?
  • Méthodes add/remove OK ?
  • JoinColumn configurée ?

Après l'implémentation

  • Migration générée et appliquée ?
  • Test dans un contrôleur ?
  • Relation bidirectionnelle fonctionnelle ?

Exercice pratique 💪

Objectif : Créer une relation Article ↔ Comment

Article : OneToMany vers Comment
Comment : ManyToOne vers Article

Étapes :

  1. Créer l'entité Article (title, content, createdAt)
  2. Créer l'entité Comment (content, author, createdAt)
  3. Ajouter la relation OneToMany dans Article
  4. Ajouter la relation ManyToOne dans Comment
  5. Générer et appliquer les migrations
  6. Tester dans un contrôleur

Pour aller plus loin 🚀

Concepts avancés à explorer :

Ressources :

Merci ! 🎉

💻

Des questions ?

N'hésitez pas à pratiquer et expérimenter !