ORM Doctrine

Первое, что делает мудрый человек, это спрашивает себя - зачем мне это нужно, вот и я решил поступить таким же образом, при знакомстве с ORM Doctrine (по факту реализующую спецификацию JPA).

Для начала я процитирую Википедию, потому что это базовые знания, без них никак.

Doctrine — объектно-реляционный проектор (ORM) для PHP, который базируется на слое абстракции доступа к БД (DBAL). Одной из ключевых возможностей Doctrine является запись запросов к БД на собственном объектно-ориентированном диалекте SQL, называемый DQL (Doctrine Query Language) и базирующийся на идеях HQL (Hibernate Query Language).

Преимущества

Существует несколько мнений, почему ORM значительно упрощает поддержку БД, например:

  1. удобно дебажить, когда переименовали или удалили поле "name" из таблицы — придется каким-то хитрым образом найти во всем проекте все места в коде, в которых запросы могут использовать это поле (в случае с ORM мы сможем поставить брейкопин на геттер и узнать, какой скрипт попытался выполнить неправильный запрос
  2. удобно переименовать поля — при использовании ORM можем воспользоваться всеми возможностями таких штук как Resharper, и автоматом переименовывать сразу во всем проекте (IDE позволяет находить все места использования поля)
  3. из коробки есть MasterSlaveConnection (конечно можно вручную можно разрулить проверяя substr(sql,0,6) == select, но зачем)
  4. Migrations Tools - инструмент управления версиями схемы бд
  5. можно переходить на другие БД — например можно перенести часть таблиц на NoSQL, и сделать это так, что ни один программист не заметит этого. По сути позволяет абстрагироваться от способа хранения объектов вообще, с лёгкостью переходя от SQL к NoSQL, memcache, файлам или REST/RPC API на удалённом сервере, оперируя на уровне модели (но такие манипуляии резко сокращают функционал работы с данными, например уже не получится сделать джоин таблиц из разных типов БД)
  6. можно настроить логирование и запросы в логах будут в виде SELECT * FROM product WHERE id IN (:ids), напомню, что PDO генерирует разные варианты одного и того же запроса, в зависимости от переданного кол-ва идентификаторов IN (?,?,?)
  7. можно кэшировать метаданные бд (схему), запросы и результаты — не самая высокая оптимальность запросов через ORM, частично компенсируется возможностью кэширования объектов с помощью своего или готово кэш-драйвера, например APCu (если у вас класстер из приложений - будьте осторожны, ведь на каждом сервере APCu свой)
  8. не требует унаследовать класс-сущность от какого-то базового класса (позволяет связать любой класс с базой данных)
  9. ORM это инструмент облегчения разделения труда разработчиков: кто хорошо шарит в SQL вообще и особенностях конкретного движка в частности — работает по «ту сторону» ORM, оптимизирует его как хочет, может нормализовывать и денормализовывать, а тот, кому дали определенную задачу создания интерфейса в программе или сайте, может вообще ничего не знать об SQL, ему лишь нужно знать, что он всегда может получить объект или коллекцию объектов, обратившись к методам вроде findById() или findAll() и сохранить результат работы методом save() или flush();
  10. EntityManager который использует паттер Data mapper (жаль, что в php при каждом запросе EntityManager пересоздается), но все равно, это лучше Active Record который нарушает принцип единственной ответственности (SRP)
  11. есть встроенная реализация транзакций — когда Вы создаете/изменяете ряд объектов и в нужный момент сохраняете их
  12. автоматическая валидация значений с помощью анотаций к полям в описании сущностей, например @Assert\Range
  13. возможность мультиязычного указания ошибки валидации, например @Assert\Type(type="integer", message="divisionKindId.integer")
  14. Doctrine может возвращать объекты частично, целиком и даже умеет возвращать данные согласно указанному PDO Fetch Modes
  15. Doctrine умеет делать принудительную или ленивую загрузку связанных сущностей (объектов):

    $query = $em ->createQuery("SELECT s FROM ASBillingBundle:Subscription s")->setFetchMode(
            "ASBillingBundle:Subscription",
            "user",
            DoctrineORMMappingClassMetadata::FETCH_EAGER
    );

  16. а еще удобно писать сложные запросы с помощю построителя запросов ORM, например выборка товаров:

$select = $db->select()->from('goods');
$select->join('pages', 'pages.idPage = goods.idPage');

if ($content) {
    $select->join('content', 'content.idContentPack = pages.idContentPack');
    $select->where('content.idLang = ?', Kernel_Locale::getCurrent()->getId());
    if ($search) {
        $select->where('content.contentName = ?', $search);
    }
}
if (!$expired) {
    $select->where('goods.goodExpirationDate > ?', time());

И давайте на чистоту - если Вы завтра решите добавить еще кое-какие поля в пару таблиц, немного поменять логику - вы легко измените ваши 38 написанных ручками запросов. Проблемы начинаются тогда, когда у вас в проекте полсотни-сотня таблиц со своими связями. Что вы сделаете в случае 462 "ручных" запросов? Правильно, вы запасетесь свободной неделькой, прошерстите все запросы, добавите/измените нужные, также поменяете обработчики данных для валидации, покопаетесь в формах, полезете в базу и добавите там все необходимое. Как следствие вы допустите массу ошибок и опечаток и еще неделю после будете отлавливать баги, из которых минимум пять все равно останутся незамеченными и всплывут уже в рабочем проекте в самое неподходящее время.

Что вы сделаете в случае с ОРМ — Вы залезете в описание схемы, добавите нужные модели/свойства, перегенерируете все, получите на выходе измененные формы, модели и структуру БД, допишете немного кода, поправите отображение форм и пойдете пить пиво с друзьями.

Кэш

Закешируем какой-нибудь запрос

$query = $em->createQuery($dql);
$query->useResultCache(true);

Очистим кэш без рестарта приложения, в котором хранится кэш:

php app/console doctrine:cache:clear-metadata
php app/console doctrine:cache:clear-query
php app/console doctrine:cache:clear-result

Недостатки

Недостатков фактически нет, но есть рекомендации, когда не стоит использовать ORM:

  1. когда Вам нужно вытащить из базы огромное количество данных, ведь создание объектов влечет расходы RAM и процессора
  2. когда вы работаете с огромным количеством запросов только на чтение, опять же, сэкономите на RAM и процессоре
  3. когда у Вас в проекте много аналитики, и нужно писать сложные SQL-запросы (делаете запросы через PDO и радуетесь приросту скорости)
  4. когда вам уже сейчас нужна полная поддержка всех фич базы, которая используется
  5. если не хотите слишком много времени тратить на изучение реализации нативных фич Вашей б.д. в ORM

Решаемые проблемы

  1. все поля (которые foreign keys) при выборке данных, оказываются Proxy-объектами (можно побороть используя Hydration Modes или $object->getValue()->getFieldName())
  2. при сохранении объектов, все поля с foreign key, должный быть объектами, т.е. нельзя явно указать user_id, значением должен быть именно объект User с заполненным значением свойства user_id ( говорят можно побороть так: EntityManager->getreference($class, $id) )

Внутренности

Коротко, паттерны из которых состоит:

Data Mapper таблица базы данных может быть представлена как PHP-класс сущности, а строка из таблицы как экземпляр объекта класса
Identity Map если ты повторно выбираешь ту же самую сущность из базы, тебе возвращается ссылка на существующую сущность. Доктрина следит чтобы каждая сущность существовала ровно в одном экземпляре, и это помогает избежать противоречий, когда есть несколько экземпляров одного объекта и непонятно в каком из них актуальные данные
Unit of Work хранит в себе сведения обо всех объектах, извлеченных, обновленных, созданных и удаленных на протяжении текущего сеанса. Задача Unit of Work следить за всеми действиями приложения, которые могут изменить БД в рамках одной бизнес-транзакции. Когда бизнес-транзакция завершается, Unit of Work выявляет все изменения и вносит их в БД, параллельно оптимизируя этот процесс.
EntityManager это классическая реализация паттерна Facade. Через его легкий API мы работаем с рядом подсистем: Unit Of Work, Query language и Repository API. Управление сущностями выполняется исключительно через API EntityManager-а. Например, когда ты делаешь изменения в сущностях, они не сохраняются автоматически, ты должен явно вызвать метод flush() и тогда произойдет следующее $this->unitOfWork->commit(); Таким образом Unit Of Work найдет все изменившиеся, новые и удаленные сущности и соответственно обновит/вставит/удалит записи в базе одной транзакцией.

Компоненты для работы с реляционными базами данных:

ORM Doctrine

Голубые блоки обозначают PHP-движок и расширения PHP, на основе которых построена Doctrine.

  • Doctrine\Common. Общая библиотека для проектов Doctrine. Этот компонент предоставляет широко используемый набор функций.

  • Doctrine\Annotations. Парсер многострочных комментариев.

  • Doctrine\Inflector. Манипуляции со строками, касающиеся изменения регистра и единственного/множественного числа.

  • Doctrine\Lexer. Базовая библиотека для лексического анализатора, который может использоваться в нисходящем синтаксическом анализе и методе рекурсивного спуска.

  • Doctrine\Cache. Библиотека для кэширования, предлагающая объектно-ориентированный API для многих бэкендов кеша.

  • Doctrine\DBAL. Абстрактный уровень БД. This is a lightweight and thin runtime layer around a PDO-like API and a lot of additional, horizontal features like database schema introspection and manipulation through an object oriented API.

  • Doctrine\Collections. Библиотека для абстракции коллекций.

  • Doctrine\ORM. Компонент для объектно-реляционного отображения. Позволяет работать с моделями сущностей объектно-ориентированным методом вместо запросов на чистом SQL.

  • Doctrine\Migrations. Миграции схем БД с использованием Doctrine DBAL. Предоставляет последовательный способ управления схемой БД и ее обновления.

  • Doctrine\DataFixtures. Данные предварительной настройки для всех менеджеров объектов Doctrine. Предоставляет фреймворк для создания данных предварительной настройки.

Компоненты для работы с NoSQL-БД:

ORM Doctrine

  • Doctrine\MongoDB - уровень абстракции Dotrine MongoDB.

  • Doctrine\MongodbODM (Object Document Mapper) предоставляет возможность устанавливать соответствие между документами NoSQL и моделями сущностей PHP.

  • Doctrine\MongoODMModule - модуль Zend Framework 3, обеспечивающий функциональность Doctrine MongoDB ODM.

  • Doctrine\CouchDB - компонент, предоставляющий API, который является оберткой для API CouchDB для доступа по HTTP.

  • Doctrine\CouchDB - компонент CouchDB Document Object Mapper. Аналогично Doctrine ORM он предоставляет возможность доступа к БД объектно-ориентированным методом.

  • Doctrine\OrientdbODM - набор библиотек PHP для использования OrientDB.

  • Doctrine\PhpcrODM - это Object Document Mapper для PHPCR.

Состояния объекта

Коротко, можно описать жизнь объекта так:

ORM Doctrine

Что дальше

Дальше, стоит напомнить про DDD (доменную модель)

ORM Doctrine

Для краткого обзора пожалуй, материала достаточно, удачки в изучении ORM.

Источники: 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 

Оцени публикацию:
  • 10,37
Оценили человек: 10
Теги : ORM, Doctrine

Похожие статьи:

Справочники и учебники:


Предложения и пожелания:
Ваше имя:
Ваш E-mail:
Сколько будет Οдин + Τри
Главная
X

youtube.com/watch?v=7hFivbgIEqk

При полном или частичном использовании материалов данного сайта, ссылка на сайт "yapro.ru" обязательна как на источник информации.
Автоматический импорт материалов и информации с сайта запрещен.
Copyrights © 2007 - 2019 YaPro.Ru

Главная » Веб-мастеру » PHP »