Guide pour les débutants
Une entité est une classe PHP qui représente une table dans votre base de données.
Imaginez une entité comme un formulaire papier :
Symfony utilise Doctrine ORM pour gérer automatiquement les interactions avec la base de données.
<?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...
}
php bin/console make:entity
php bin/console make:migration
php bin/console doctrine:migrations:migrate
Un auteur écrit plusieurs livres
Un livre a un seul auteur
Un étudiant suit plusieurs cours
Un cours a plusieurs étudiants
Une personne a un passeport
Un passeport appartient à une personne
Dans une relation OneToMany / ManyToOne :
OneToMany = côté "un"
L'auteur a ses livres
ManyToOne = côté "plusieurs"
Le livre a son auteur
#[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();
}
}
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;
}
#[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;
}
}
#[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...
}
#[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 !
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();
}
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;
}
#[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 !');
}
Article : OneToMany vers Comment
Comment : ManyToOne vers Article
Des questions ?
N'hésitez pas à pratiquer et expérimenter !