Doctrine ORM Basics
Object-relational mapper. You write entities; Doctrine translates them to SQL via a unit-of-work pattern.
- Entities are plain PHP classes annotated with `#[ORM\Entity]` and `#[ORM\Column]` attributes.
- The `EntityManager` is the central API: `persist()` schedules an insert/update, `remove()` schedules a delete, `flush()` runs queued operations as one transaction.
- Repositories are the read side: `EntityManager::getRepository(User::class)` or inject a custom repository class.
- Doctrine tracks changes automatically — modifying an attached entity then calling `flush()` writes the diff.
- Lifecycle callbacks (`#[ORM\PrePersist]`, `#[ORM\PreUpdate]`) run inside the unit-of-work; keep them simple.
#[ORM\Entity]
class User
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private string $email;
}
Common gotchas
- Forgetting to `flush()` is the #1 "why didn't my change save?" cause.
- Lifecycle callbacks fire only inside the unit-of-work — they do NOT fire on `UPDATE` queries you write by hand via DQL.