Doctrine ORM

Doctrine is the object-relational mapper (ORM) Symfony pairs with by default. It's worth being clear about this: Doctrine is a separate project, not something Symfony itself builds or maintains — but the two are so commonly used together, and integrate so smoothly, that "Symfony + Doctrine" is effectively the standard combination you'll meet in almost every Symfony application. Doctrine's job is to let you work with plain PHP objects instead of writing raw SQL, translating method calls like persist() and find() into database queries behind the scenes.

Entities

An Entity is a plain PHP class annotated with PHP 8 attributes that describe how it maps to a database table. Attributes use the #[...] syntax and sit directly above the class or property they describe — Doctrine reads them to know which table an object belongs to and which column each property corresponds to.

AttributeWhat it does
#[ORM\Entity]Marks the class itself as a Doctrine entity
#[ORM\Table(name: 'articles')]Names the database table the entity maps to
#[ORM\Column(type: 'string')]Marks a property as a mapped column, with its column type

A Worked Example: The Article Entity

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'articles')]
class Article
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string')]
    private string $title;

    #[ORM\Column(type: 'text')]
    private string $content;

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

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle(string $title): static
    {
        $this->title = $title;

        return $this;
    }

    public function getContent(): string
    {
        return $this->content;
    }

    public function setContent(string $content): static
    {
        $this->content = $content;

        return $this;
    }
}

Each private property has a matching getter (and, other than id, a setter) — this is the plain-PHP-object shape Doctrine expects, and the same shape the Form component (covered on the Forms page) knows how to bind to automatically.

Generating Entities and Migrations

You rarely write an entire entity by hand. Symfony's console (see Console Commands) ships a code generator that scaffolds entities and the migration files that create or alter their matching tables:

php bin/console make:entity

php bin/console make:migration

make:entity interactively asks for the class name, its properties, and their types, then writes (or updates) the entity class for you. make:migration compares your current entity mappings against the actual database schema and generates a migration file containing the SQL needed to bring the database in line.

The EntityManager

The EntityManager is the central object Doctrine uses to persist and query entities. You hand it plain PHP objects, and it takes care of turning that into the right SQL.

<?php
$article = new Article();
$article->setTitle('Getting Started with Doctrine');
$article->setContent('Doctrine maps PHP objects to database rows.');

// Saving a new entity
$entityManager->persist($article);
$entityManager->flush();

// Fetching an entity by its id
$article = $repository->find($id);

persist() tells Doctrine "I want this object saved eventually" — it doesn't hit the database yet. flush() is what actually executes the queued SQL (an INSERT in this case). Reading works the other way: a repository (one per entity) gives you finder methods like find($id) to fetch an existing row back as a fully hydrated Article object.

Next steps: The EntityManager needs a database to actually connect to. Head to Database Configuration to set the connection string and run your first migration.