Перейти к основному содержимому

Что такое Weaver ORM?

Weaver ORM — это объектно-реляционный маппер (ORM) для PHP 8.4+ и Symfony-приложений, построенный на единственном принципе: доменные объекты не должны ничего знать о базе данных. Никаких аннотаций на классах сущностей, никакой генерации прокси-объектов, никакой рефлексии во время выполнения — только чистые PHP-объекты и явные классы-маппера, которые выполняют перевод между объектами и SQL.

Проблемы, которые решает Weaver

Прокси-объекты Doctrine

Doctrine оборачивает каждую связанную сущность в прокси-класс, который перехватывает обращение к свойствам, чтобы при первом доступе выполнить SQL-запрос. В традиционных циклах «запрос/ответ» это незаметно, но такой подход незаметно порождает паттерн N+1 запросов и затрудняет отладку (var_dump($post->getAuthor()) выведет прокси, а не объект User).

В долго живущих PHP-воркерах (RoadRunner, FrankenPHP, Swoole, Symfony Messenger) EntityManager накапливает устаревшее состояние между запросами и должен вручную сбрасываться на каждой границе запроса — это легко упустить, а результирующая ошибка тяжело диагностируется.

Гидрация через рефлексию

Doctrine использует ReflectionProperty для прямой записи в приватные/защищённые свойства сущности, обходя доменную логику. При каждом запросе PHP-атрибуты заново разбираются или берутся из прогретого кэша; прокси-классы должны существовать на диске.

Неограниченная карта идентификаторов

EntityManager Doctrine хранит каждую загруженную сущность в памяти на протяжении всего запроса. Загрузка больших результирующих наборов приводит к неограниченному росту потребления памяти. Обходное решение — $em->clear() — отсоединяет всё, включая сущности, которые вы забыли переприсоединить.

Что Weaver делает иначе

Weaver построен на четырёх принципах:

  1. Чистые PHP-объекты в роли сущностей. Класс User не имеет никаких зависимостей от ORM. Ни атрибутов, ни базового класса, ни интерфейсов. Это чистый объект-значение или доменный объект, который можно тестировать без запуска Symfony.

  2. Явные классы-маппера. Отдельный класс UserMapper описывает, как User отображается на таблицу users. Типы колонок, связи, первичные ключи — всё в одном месте, всё на чистом PHP, полностью просматриваемо через grep и поддаётся статическому анализу.

  3. Никаких прокси, никакой неявной ленивой загрузки. Связи всегда загружаются явно через ->with(['relation']). Вы всегда точно знаете, какой SQL выполняется и когда.

  4. Безопасность для воркеров по умолчанию. Маппера не имеют состояния и загружаются один раз на процесс воркера. Каждый HTTP-запрос или задание Messenger получает собственное EntityWorkspace (единица работы), поэтому между запросами нет разделяемого изменяемого состояния.

Ключевые отличия на первый взгляд

ВозможностьDoctrine ORMWeaver ORM
Генерация прокси-классовТребуетсяНе нужна
Рефлексия во время выполненияДаНикогда
Ленивая загрузкаНеявная (через прокси)Только явная
Аннотации/атрибуты сущностейНа классе сущностиОтдельный класс-маппер
Перезапуск воркера при сбросеДаНет
Предотвращение N+1Ручной JOIN FETCHПринудительно через with()
Память на 10k строк~48 МБ~11 МБ
Время гидрации 10k строк~420 мс~95 мс
PHPStan / статический анализЧастичный (магические прокси)Полный (явные маппера)

Бенчмарки: PHP 8.4, PostgreSQL 16, Ubuntu 22.04, 10 000 строк User со связью Profile. Результаты варьируются в зависимости от железа и сложности запросов.

Обзор архитектуры

Entity (чистый PHP-класс — без связи с ORM)

└── Mapper (имя таблицы, колонки, связи, hydrate/extract)

└── EntityWorkspace → QueryBuilder → PDO/DBAL

EntityWorkspace заменяет EntityManager из Doctrine. Это единица работы (unit of work) в рамках одного запроса, которая отслеживает, какие сущности нужно вставить, обновить или удалить при вызове flush(). Поскольку область видимости ограничена одним запросом, утечки карты идентификаторов между запросами нет.

Поддержка PyroSQL

Weaver поставляется с опциональной поддержкой PyroSQL — высокопроизводительного встроенного аналитического SQL-движка. PyroSQL можно использовать как реплику для чтения для агрегатных запросов, отчётности и операций с большими объёмами данных без нагрузки на основную реляционную базу данных. См. раздел PyroSQL для подробностей.

Требования

ЗависимостьМинимальная версия
PHP8.4
Symfony7.0
doctrine/dbal4.0 (только уровень подключения)
MySQL8.0
PostgreSQL14
SQLite3.35

Опциональные:

  • symfony/messenger — асинхронная публикация событий и паттерн Outbox
  • symfony/cache — кэширование результатов запросов
  • mongodb/mongodb + ext-mongodb — поддержка маппера документов MongoDB

Чем Weaver не является

Weaver не является прямой заменой Doctrine. Если вы активно используете DQL, Criteria API или миграции на основе атрибутов, потребуется переписать этот слой. Weaver лучше всего подходит для новых проектов на Symfony 7+ или приложений, мигрирующих с Doctrine, которым нужна явная, предсказуемая работа с SQL и безопасность для воркер-процессов.