> Какой паттерн реализует Doctrine ORM (PHP)

Уровень: middle · Роль: backend · Язык: PHP · Категория: Технические вопросы

Компании: vanta

Стек: PHP

> Пример ответа

Короткий ответ

Doctrine ORM реализует паттерн Data Mapper. Сущности в Doctrine - это обычные PHP-объекты, не знающие о базе данных. Маппер (EntityManager) отвечает за синхронизацию состояния объектов с БД, скрывая SQL и детали хранения. Это отличает Doctrine от Active Record (например, Eloquent в Laravel), где объект сам управляет своим сохранением.

Подробное объяснение

Data Mapper - это архитектурный паттерн, который разделяет доменную модель и слой хранения данных. В Doctrine:

  • Сущности - чистые PHP-классы с аннотациями/атрибутами, описывающими маппинг полей на колонки таблиц.
  • EntityManager - центральный компонент, который отслеживает изменения объектов (unit of work), генерирует SQL и выполняет запросы.
  • Repository - дополнительный слой для выборки данных, инкапсулирующий запросы.

Ключевые особенности реализации:

  • Identity Map - EntityManager кэширует загруженные объекты, гарантируя, что одна строка БД соответствует одному экземпляру объекта в рамках одного запроса.
  • Unit of Work - отслеживает изменения (dirty checking) и выполняет flush() для синхронизации всех изменений одним батчем.
  • Lazy Loading - связанные сущности подгружаются только при обращении к ним, через прокси-объекты.

Этот подход даёт полную свободу в проектировании доменной модели, но требует явного управления жизненным циклом объектов (persist, flush, clear).

На практике

В реальном коде это выглядит так:

  • Создаёте сущность через new User(), заполняете поля, вызываете $em->persist($user) - объект попадает под управление EntityManager.
  • При вызове $em->flush() Doctrine сравнивает текущее состояние объекта с состоянием из identity map и выполняет INSERT/UPDATE.
  • Для чтения используете репозиторий: $em->getRepository(User::class)->find($id).
  • Важно: без flush() изменения не сохраняются - это осознанный trade-off в пользу батчевой записи.
  • В отличие от Active Record, вы не можете просто вызвать $user->save() - нужен внешний маппер.

Пример кода

PHP
// Сущность - чистый PHP-объект
#[Entity]
class User
{
#[Id, Column(type: 'integer')]
private int $id;
#[Column(type: 'string')]
private string $name;
public function setName(string $name): void
{
$this->name = $name;
}
}
// Использование Data Mapper
$em = $entityManager;
// Создание нового объекта
$user = new User();
$user->setName('Alice');
$em->persist($user); // объект под управлением
$em->flush(); // реальный INSERT
// Обновление - объект уже отслеживается
$user->setName('Bob');
$em->flush(); // UPDATE, только если поле изменилось
// Чтение через Repository
$repo = $em->getRepository(User::class);
$found = $repo->find(1);

Как отвечать на собеседовании

Начните с прямого ответа: "Doctrine реализует Data Mapper". Затем кратко объясните суть паттерна: разделение доменной модели и инфраструктуры хранения. Упомяните, что EntityManager - это маппер, а сущности не знают о БД. Добавьте пару деталей реализации: identity map, unit of work, lazy loading. Если спросят про отличия - сравните с Active Record (Eloquent), подчеркнув, что в Doctrine нет метода save() на самой сущности. Завершите примером из практики: как выглядит persist/flush цикл.

Что проверяет интервьюер

  • Понимаете ли вы разницу между Data Mapper и Active Record.
  • Знаете ли вы, как работает EntityManager и зачем нужен flush().
  • Понимаете ли вы жизненный цикл сущностей (managed, detached, new).
  • Осознаёте ли вы последствия выбора паттерна: больше контроля, но больше кода и явное управление.
  • Знаете ли вы про identity map и unit of work - это показывает глубину.

Типичные ошибки

  • Ответ "Active Record" - это грубая ошибка, характерная для людей, перешедших с Laravel.
  • Утверждение, что сущности "знают" о таблицах - в Doctrine маппинг описывается отдельно, сущность остаётся чистой.
  • Путаница между persist() и flush() - persist только регистрирует объект, flush выполняет запросы.
  • Игнорирование identity map - если дважды запросить одну сущность, получите один и тот же объект, а не две копии.
  • Забывают упомянуть, что Data Mapper позволяет использовать не только таблицы, но и другие источники данных (например, внешние API) с минимальными изменениями в домене.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью