ORM (Object-Relational Mapping, рус. Объектно-реляционное отображение) — это технология, которая связывает базы данных с концепциями объектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных». С помощью ORM упрощается процесс сохранения объектов в реляционной базе данных и их последующего извлечения, так как она автоматизирует преобразования данных между двумя различными форматами.
По своей сути, ORM обеспечивает работу с базой данных на уровне объектов, что подразумевает соответствие структуры и данных в БД объектам кода. В ходе работы с этими объектами происходят изменения как в базе данных, так и в коде. Основные принципы функционирования ORM включают следующие тезисы:
Каждой таблице базы данных соответствует отдельный класс. Например, для таблицы articles создается класс Article.
В классах описываются свойства объектов, которые соотносятся с полями таблицы. Например, поле authorId связано с author_id в таблице articles.
Каждый экземпляр класса представляет одну запись в базе данных, следовательно, объект класса Article соответствует строке в таблице articles.
Суть ORM заключается в том, что объекты находят свое отражение в базе данных, позволяя выполнять операции на уровне объектов и соответствуя принципам объектно-ориентированного подхода.
Среди причин, привлекающих разработчиков к ORM, можно выделить несколько ключевых аспектов:
Большинство библиотек ORM обеспечивает проверку данных при их добавлении и обновлении.
ORM предоставляет возможность сопоставлять названия столбцов в базе данных с названиями полей в коде, что удобно при работе с чужими базами данных, где могут быть нестандартные имена.
Они эффективно обрабатывают отношения между таблицами, автоматически загружая связанные элементы при извлечении записей с отношениями 1:m, избавляя от необходимости выполнять дополнительные запросы вручную.
Тем не менее, сопоставление объектов и реляционных баз данных может быть сложным делом. Написание SQL-запросов и работа с базой данных на низком уровне часто требует много времени и усилий. В этой ситуации на помощь приходят ORM-библиотеки для PHP, предоставляющие интуитивные и простые решения для управления базами данных в приложениях.
Давайте рассмотрим несколько хорошо известных библиотек ORM для PHP:
Doctrine ORM
Doctrine — одна из наиболее популярных библиотек, которая обеспечивает мощное и гибкое сопоставление между объектами и таблицами базы данных, поддерживая различные форматы отображения, такие как аннотации, XML и YAML. Она предлагает расширенные возможности для написания запросов и поддерживает транзакции и кэширование. Активное сообщество и разнообразие функций делают Doctrine отличным выбором для использования.
Eloquent ORM
Eloquent ORM — это библиотека, созданная для фреймворка Laravel. Она выделяется простым и элегантным синтаксисом, что делает работу с ней более приятной. Eloquent предлагает удобный API для выполнения операций: запросов, вставок, обновлений и удалений записей, а также поддерживает быстрое извлечение данных и управление взаимосвязями. Если вы используете Laravel, Eloquent станет отличным выбором.
Propel ORM
Propel ORM — еще одна популярная библиотека для работы с PHP, ориентированная на производительность. Она построена на основе PDO PHP и обеспечивает удобный API для взаимодействия с базами данных. Propel поддерживает модель active record и data mapper, а также предлагает функции миграции схемы базы данных и обратного проектирования. Если для вашего приложения критична производительность, стоит задуматься о Propel.
Теперь, когда вы ознакомились с некоторыми известными библиотеками ORM для PHP, пришло время выбрать подходящую именно для ваших нужд. Учитывайте ваши конкретные требования, такие как производительность, простота использования и поддержка сообщества. Обратите внимание на синтаксис и функции, которые важны для вашего проекта, возможно, стоит протестировать несколько библиотек, чтобы найти наиболее подходящую.
Также важно глубже разобраться в принципах работы разных библиотек и понять их ключевые различия. Например, Eloquent (используемый в Laravel) реализует шаблон проектирования active record, тогда как Doctrine (используемый в Symfony) применяет паттерн data mapper.
Давайте разберем эти отличия на примере:
Active Record
В шаблоне Active Record экземпляр модели тесно связан с отдельной записью из базы данных. Реализация модели наследует базовую модель и свойства объекта автоматически соотносятся к столбцам таблицы. Например, при использовании Eloquent, в базовой модели применяется метод __get(), и чтобы получить электронное письмо от пользователя, необходимо написать следующий код:
echo $user->email;
Внутри выполняется соответствующим образом:
public function __get($key)
{
return $this->getAttribute($key);
}
И:
public function getAttribute($key)
{
if (! $key) {
return;
}
// Если атрибут существует в массиве атрибутов или имеет мутатор "get", мы будем
// получать значение атрибута. В противном случае мы будем действовать так, как если бы разработчики
// запрашивали значение отношения. Это относится к обоим типам значений.
if (array_key_exists($key, $this->attributes) ||
$this->hasGetMutator($key)) {
return $this->getAttributeValue($key);
}
// Здесь мы определим, содержит ли сам базовый класс модели данный ключ
// поскольку мы не хотим рассматривать ни один из этих методов как отношения, потому что
// все они являются вспомогательными методами, и ни один из них не является отношением
if (method_exists(self::class, $key)) {
return;
}
return $this->getRelationValue($key);
}
Таким образом, модель получает запись из базы данных, загружая её в массив, и просто возвращает необходимые значения. Чтобы сохранить изменения в модели, нужно вызвать метод:
$user->save();
В данной реализации присутствует нарушение принципа единой ответственности: объект сам занимается своим сохранением. Тем не менее, стратегия Active Record может быть вполне приемлемой. Все зависит от типа создаваемого приложения. Если речь идет о простых CRUD-приложениях без серьезной бизнес-логики, этот шаблон будет предпочтительным решением. К тому же, одно из главных достоинств метода Active Record — это возможность быстрой разработки. В отличие от него, использование Data Mapper требует решения большего количества задач, что делает Active Record также удобным для создания прототипов.
Data Mapper
В отличие от Active Record, модель в паттерне Data Mapper представляет собой независимую сущность, которая не имеет информации о том, как и где она сохраняется. Это становится значительным преимуществом при работе со сложными моделями. В этом подходе свойства объекта четко определены, что позволяет модели автоматически расширяться, так как все свойства должны быть реализованы и детализированы соответственно.
<?php
use Doctrine\ORM\Mapping AS ORM;
/**
* @ORM\Entity
* @ORM\Table(name="users")
*/
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $email;
...
}
Модели сохранения выглядят примерно так:
$entityManager->persist($user);
$entityManager->flush();
Если модель является анемичной, то есть содержит лишь установщики и геттеры, использование Active Record может быть оптимальным вариантом. Однако при разработке приложения важно тщательно продумать архитектуру, так как даже самую сложную модель можно сделать анемичной, делегировав ее функции службам, контроллерам или другим компонентам системы.
Следует отметить, что выбор между Active Record и Data Mapper зависит от конкретных требований проекта и предпочтений команды разработчиков. Если необходимо реализовать простое приложение с ограниченной бизнес-логикой, Active Record может оказаться более целесообразным благодаря своей простоте и быстроте в разработке. Однако, если вы планируете создать сложное или масштабируемое приложение, стоит задуматься об использовании Data Mapper, так как он лучше справляется с изменениями в бизнес-логике и позволяет эффективно поддерживать код.
Помимо вышеназванных библиотек и паттернов, стоит также учитывать дополнительные аспекты при работе с ORM. Например, важным моментом является производительность. Хотя ORM значительно упрощает взаимодействие с базами данных, неправильно сконструированные запросы или неоптимизированный код могут привести к ухудшению производительности приложения. Поэтому важно знать, как правильно формировать запросы и избегать «липких» императивных конструкций. Например, избыточная выборка данных (eager loading) может влиять на производительность, если она используется без должного контроля.
Также стоит упомянуть о кэшировании данных, которое является важной частью обеспечения высокой производительности. Многие ORM, такие как Doctrine, поддерживают различные подходы к кэшированию, которые помогают значительно снизить количество обращений к базе данных и ускорить работу приложения. Это особенно актуально для высоконагруженных систем, где каждая миллисекунда на счету.
Кроме того, разработка с использованием ORM требует глубокого понимания концепций и основ реляционных баз данных. Необходимо хорошо разбираться в нормализации и денормализации, индексах и связывании таблиц, что позволит наиболее эффективно использовать возможности ORM. Применение неправильной модели данных может привести к ухудшению производительности и усложнению логики приложения.
Например, если критична производительность и использование ORM оказывается слишком абстрактным, стоит рассмотреть вариант применения чистого SQL или Query Builder, доступного в Laravel, без полноценного использования ORM. Это позволит добиться полного контроля над запросами и в некоторых случаях обеспечит более высокую производительность благодаря исключению слоя абстракции.
В заключение нужно сказать, что использование ORM в PHP и выбор конкретной библиотеки — это решение, которое требует взвешенного подхода и анализа потребностей вашего проекта. Понимание ключевых принципов, паттернов, преимуществ и недостатков каждого из решений поможет сократить время на разработку, повысить производительность и облегчить поддержку вашего приложения в будущем.
Комментарии (16)
AlexeyPolunin
25.09.2024 13:43+7Для максимальной производительности наверное не надо использовать никакой ORM. Но я знаю, что я в меньшинстве.
DmitriySun
25.09.2024 13:43+1Никак нет. Вы в большинстве. Но когда идет разговор про стоимость часа разработки, то вспоминают про ОРМ - они же в CRUD себя неплохо показывают? А что будет, когда логика перестает быть CRUD? На этом моменте всем не важно, главное стоимость часа, и время.
ОРМ это скорее плохо. Но если вы пишете записную книжку, или что-то документ-ориентированное - ОРМ вам помощник.P.S. И еще ОРМ снижает ФЗП. Зачем платить разработчику со знанием SQL, когда можно взять разработчика без знания SQL?
Dadadam999
25.09.2024 13:43Брать человека не знающего SQL, для работы с БД - это уровень фразы "просчитался, но где".) Большинство ORM по умолчанию даже нормальное индексирование не делают, о чём человек, не знающий SQL знать не будет, как и банально не сможет написать специфический функционал.)
FanatPHP
25.09.2024 13:43Вы какую-то чушь здесь написали. ORM, который "не требует знания SQL" - это тот самый CRUD на 4 запроса. Людей, которые занимаются веб-программированием не зная SQL на этом уровне, можно себе представить только в полемических фантазиях.
При этом для любого чуть более сложного запроса ORM надо понимать, как его писать. Даже с использованием ORM. Тупо на уровне какие ключевые слова писать.
SerafimArts
25.09.2024 13:43Не всегда. Это примерно как Rust vs Assembly. В теории (и на практике) можно написать Low-Level код, который бы работал быстрее и эффективнее ORM, однако на практике же при масштабировании без наличия UoW, Identity Map (IM), всяких ленивых "where in" отношений и прочего можно запросто получить большую мусорку, особенно когда общение начинается между разными контекстами через какую-нибудь шину и одна часть системы, реагирующая на какое-то событие/команду не знает ничего о другой.
Ну, например, аутентификационная инфраструктура дёргает юзера, а потом где-то в апп сервисе он же дёргается через репозиторий уже. Без IM получим лишний запрос сразу (а то и несколько, если юзер тянет за собой ещё что-то).
Так что я бы переформулировал как "для максимальной производительности, при наличии свободного времени написать половину того, что есть в ORM и достаточной квалификации".
Dadadam999
25.09.2024 13:43+1Нет, не в меньшинстве.)
ORM по большей части используется для удобства и стандартизации работы с бд, а не ради производительности. Поэтому, как всегда всё сводится к поиску золотой середины под каждый проект в отдельности.
FanatPHP
25.09.2024 13:43Почему-то всегда дихтомия, или-или. Или штепсель вдребезги, или розетка пополам. Почему не вместе-то?
90% кода типичного веб-приложения и так не вызывает никаких проблем с производительностью. Получить запись по первичному ключу. Получить полсотни записей простым джойном. Такого рода операции либо вообще не требуют никакой "оптимизации", либо прекрасно оптимизируются средствами ORM. Но при этом объём и время написания кода сокращаются в разы.
При этом действительно тяжёлые случаи никто не запрещает переписать на чистый SQL. Если цель - быстро разработать стабильно работающее приложение, а не устроить очередной холивар.
AlexeyPolunin
25.09.2024 13:43Да конечно, если работает нормально и в нужном бюджете - абсолютно все равно.
Bigata
25.09.2024 13:43А что разработчики вообще разучились в pdo и хотя бы middle уровень sql запросов?
Да уж, скоро no-code во всей красе выплюнется
FanatPHP
25.09.2024 13:43+2Статья - редкая халтура.
Сочинение студента-двоечника, чтобы препод откопался. На вопрос "как выбрать" не отвечает вообще никак. Ну только если не считать ответом классические камлания "выбор - вопрос сложный, к нему надо подходить ответственно". Плюс обязательная отсебятина и пять кубометров воды.
Ещё статья до боли напоминает индусский спам, который одно время потоком шёл на Реддите, буквально по 2-3 поста в неделю: как выбрать фреймворк, как выбрать ORM, как выбрать язык... При этом отличительные особенности каждого из продуктов берутся от балды, без какой-либо системы или привязки к реальности. Что неудивительно, поскольку для написания реальной статьи надо все эти продукты знать. А этим может похвастаться далеко не каждый профессиональный разработчик.
В итоге вместо осмысленного выбора мы получаем мычание, общие слова про ORM, равномерно размазанные по всем трем участникам соревнований:
Про Доктрину написано, что она "предлагает расширенные возможности для написания запросов и поддерживает транзакции и кэширование". Как будто другие две этого не предлагают.
"Eloquent предлагает удобный API для выполнения операций: запросов, вставок, обновлений и удалений записей, а также поддерживает быстрое извлечение данных и управление взаимосвязями". Ну просто киллер-фича, отсутствующая в других библиотеках.
Про Propel указано, что она "построена на основе PDO". Это, конечно, сразу выделяет её на фоне остальных! А также высокая производительность. Птица-говорун отличается умом и производительностью. И популярностью. Вот только почему-то "медленную" Доктрину устанавливают в 50 раз чаще, чем "быстрый" Пропель. У которого статистика использования говорит о популярности на уровне поддержки легаси проектов.
Собственно, отдельные элементы статьи наводят на мысль, что исходно это и есть небрежно переведённый индусский спам. "Чтобы получить электронное письмо от пользователя", ага.
Очень жаль, что компания решила войти
вайтина Хабр с такой позорной халтурой. Если вы действительно такие монстры, то у вас должен быть миллион реальных юзкейсов, про которые и надо писать статьи. Ну а если вы только ещё собираетесь становиться монстрами, то стоит сначала немного подучить матчасть, а потом уже пытаться делиться своими знаниями с окружающими.
savostin
Они скорее неэффективно создают связывающие запросы выполняя зачастую ненужные дополнительные запросы автоматически под капотом так, что у DBA потом волосы дыбом.
Romatio
В Eloquent, н-р, это регулируется. Написал ->with('relation:id,name') - загрузил отношение, можно даже поля выбрать, какие надо. С доктриной печальней.
savostin
То что язык описания у ORM красивый и лаконичный никто ж не спорит. Вопрос в том, что в итоге в виде SQL получается, какие есть средства воздействия на это и как часто этими средствами ORM-евангелисты пользуются.