Comments 62
А почему топик не обозначить как «перевод»?
Во вторых выводы какие-то оч странные. Как будто это мы сами всегда пишем реализации ОРМ.
Если мы работаем с Доктриной, мы вряд ли будем дописывать к ней слой ActiveRecord, работая с ОРМ в других фреймворках (Laravel, Yii), дописывай сколько угодно, а обьект всё равно будет знать как себя сохранить, что совершенно нивелирует паттерн датамаппер. О чем эта статья? Репозитории — это хорошо, ооокей?
— Я кстати подозреваю что статью писал ларавельщик. У них очень печально с аргументацией зачем нужен датамаппер и почему он лучше.
Во вторых выводы какие-то оч странные. Как будто это мы сами всегда пишем реализации ОРМ.
Если мы работаем с Доктриной, мы вряд ли будем дописывать к ней слой ActiveRecord, работая с ОРМ в других фреймворках (Laravel, Yii), дописывай сколько угодно, а обьект всё равно будет знать как себя сохранить, что совершенно нивелирует паттерн датамаппер. О чем эта статья? Репозитории — это хорошо, ооокей?
— Я кстати подозреваю что статью писал ларавельщик. У них очень печально с аргументацией зачем нужен датамаппер и почему он лучше.
-1
Ну плюсы/минусы спорные, наверное, вы правы.
Мне больше была интересна часть про их принципиальные отличия, ибо я очень слабо их различал.
Я не нашел как обозначить топик переводом, у меня есть только вариант «Обучающий материал» или я просто не там ищю, подскажите?
Мне больше была интересна часть про их принципиальные отличия, ибо я очень слабо их различал.
Я не нашел как обозначить топик переводом, у меня есть только вариант «Обучающий материал» или я просто не там ищю, подскажите?
0
Как-то так: minus.com/l4aPf83jiwIIZ
Да, если что я плюсик вашему посту поставил. А мои претензии к автору оригинального поста. Обзор паттернов это хорошо, но «нужно юзать репозиторий, потому что это модно» — явно не аргумент. Хотелось бы, чтобы в РНР сообществе прекратили делить паттерны на модные/не модные.
Да, если что я плюсик вашему посту поставил. А мои претензии к автору оригинального поста. Обзор паттернов это хорошо, но «нужно юзать репозиторий, потому что это модно» — явно не аргумент. Хотелось бы, чтобы в РНР сообществе прекратили делить паттерны на модные/не модные.
0
Спасибо, но у меня этого нет, наверное потому что пост перекочевал из песочницы.
Да я то с вами согласен, я вообще против фанатизма любого рода, надо как-то больше на задачи ориентироваться, — где-то data mapper, где-то active record использовать, а где-то и вообще PDO и SQL хватит и не нужно усложнять.
Да я то с вами согласен, я вообще против фанатизма любого рода, надо как-то больше на задачи ориентироваться, — где-то data mapper, где-то active record использовать, а где-то и вообще PDO и SQL хватит и не нужно усложнять.
+1
Здесь не вопрос Модно-Немодно.
Выбор конкретного подхода Active Record vs Data Mappers + Repository должен вытекать из бизнес задач.
Дело в том, что Active Record является фактический неотъемлемой частью RAD (rapid application development), и цель его быстро развернуть уровень доменных моделей (читай бизнес логики) по существующей схеме базы (как правило, небольшой). Фактически все RAD проекты строятся по принципу «Database First» — т.е. мы вначале проектируем схему БД, как-то ее декларируем (Json, Yaml, SQL, ini) и через эту декларацию фреймворк генерирует нам доменные модели с уже реализованым CRUD функционалом через механизм scaffolding-a.
Это хорошо работает на маленьких-средних проектах с небольшими зависимостями. Если же проект большой и людей, которые работают на нем много, и чтобы поддерживать все в рабочем состоянии эти люди хотят покрывать код тестами, то тут начинаются проблемы…
Поэтому в последнее время такую большую популярность и обрел DDD (Domain driven design), который декларирует одним из своих основных принципов persistence ignorance и «Code First». При этому мы получаем все те плюшки, которые автор описал в статье (SOLID, доменные модели не знают вообще, что их куда-то сохраняют, поэтому становятся тонкими и содержат только бизнес логику). Развивая идею дальше мы приходим к тому, что в таком виде можно легко реализовать Unit of Work паттерн, и разгрузить слой приложения (читай контроллеров) от обязанностей сохранять и контролировать что модели сохраняются и обновляются корректно. А эта обязанность переходит в инфраструктурный уровень. Но при этом, да, сложность проекта становится выше.
Выбор конкретного подхода Active Record vs Data Mappers + Repository должен вытекать из бизнес задач.
Дело в том, что Active Record является фактический неотъемлемой частью RAD (rapid application development), и цель его быстро развернуть уровень доменных моделей (читай бизнес логики) по существующей схеме базы (как правило, небольшой). Фактически все RAD проекты строятся по принципу «Database First» — т.е. мы вначале проектируем схему БД, как-то ее декларируем (Json, Yaml, SQL, ini) и через эту декларацию фреймворк генерирует нам доменные модели с уже реализованым CRUD функционалом через механизм scaffolding-a.
Это хорошо работает на маленьких-средних проектах с небольшими зависимостями. Если же проект большой и людей, которые работают на нем много, и чтобы поддерживать все в рабочем состоянии эти люди хотят покрывать код тестами, то тут начинаются проблемы…
Поэтому в последнее время такую большую популярность и обрел DDD (Domain driven design), который декларирует одним из своих основных принципов persistence ignorance и «Code First». При этому мы получаем все те плюшки, которые автор описал в статье (SOLID, доменные модели не знают вообще, что их куда-то сохраняют, поэтому становятся тонкими и содержат только бизнес логику). Развивая идею дальше мы приходим к тому, что в таком виде можно легко реализовать Unit of Work паттерн, и разгрузить слой приложения (читай контроллеров) от обязанностей сохранять и контролировать что модели сохраняются и обновляются корректно. А эта обязанность переходит в инфраструктурный уровень. Но при этом, да, сложность проекта становится выше.
+1
Ваш комментарий стоит всего этого поста. Если бы тот же уровень аргументации был в основном посте… Но увы, в самом посте нет никаких намеков на то что автор хорошо понимает оба паттерны и все вытекающие. «дайте ему шанс, — вам должно понравиться.» звучит как «попробуй, это модно в нынешнем сезоне!»
0
Недостатки DataMapper надуманные.
Дольше думать над чем? Управление сущностями ничем не отличается, кроме
Дольше думать над чем? Управление сущностями ничем не отличается, кроме
$dm->save($user); $user->save();
Но это на мой взгляд вообще не отличие. Ровно как и преимущество DM над AR в том что невозможно измнить mapper, просто не надо хардкодить PDO в конструкторе вот и все. На основе той же доктрины можно сделать AR, они сами описали в кукбуке этот момент.+1
В целом я с вами согласен, что при использовании, по большому счету, все равно, у какого объекта вы save() вызываете. Но все же, если не рассматривать экзотические варианты, вроде AR поверх Doctrinы, то как правило, DM — получается более сложной абстракцией со всеми вытекающими последствиями. Но, наверное, это для вас будет иметь значение только, если вы разработчик, а не пользователь.
0
Не понимаю какие вытекающие, если честно)
0
Уровень абстракции повышается, сложность стало быть. Но это больше разработчиков касается конечно, чем пользователей.
0
Сложность бывает разная. Имхо, десять простых классов поддерживать намного проще, чем один «божественный».
0
У Active Record постоянно возникают проблемы при сериализации, так как подключение к Базе данных сериализовать нельзя. Это одна из кючевых аргументов против Active Record. При этом, если подключение не сериализовать (__sleep, __wakeup), то нужно далее вручную его подсунуть, а при условии использования ServiceLocator'ов это становится проблематичным.
-1
оО вот уж не думал что это проблема. Зачем тогда придумали интерфейс Serializable? Просто раз уж AR — кеширование должно быть через сам объект статическим методом
+1
Есть ситуация, нужно Entity засунуть в Сессию (Entity юзверя залогиненного).
Если использовать Active Record, то нужно при сериализации убрать оттуда подключение к БД.
При восстановлении из сериализованных данных, соединения в User Entity нет. Его можно взять из ServiceLocator'а, но в момент восстановления данных ServiceLocator неоткуда получить (Singleton — не предлагать).
В итоге получается ситуация, что соединение засовывается вручную, и это нужно постоянно контролировать.
Если использовать Active Record, то нужно при сериализации убрать оттуда подключение к БД.
При восстановлении из сериализованных данных, соединения в User Entity нет. Его можно взять из ServiceLocator'а, но в момент восстановления данных ServiceLocator неоткуда получить (Singleton — не предлагать).
В итоге получается ситуация, что соединение засовывается вручную, и это нужно постоянно контролировать.
0
А в чем проблема инжектить в AR-сущность кешер и загружать из кеша?
0
А зачем танцевать с бубном, если можно задачу просто облегчить? У Entity есть Аннотации, по которым можно спокойно определить, и что за таблица, и управляющий Репозиторий.
Рекомендую почитать про Doctrine ORM, и причины почему Symfony сделала по дефолту (вместо Prorel) именно Doctrine.
Конечно в реальных проектах Entity Manager у Doctrine ORM очень прожорливый (ну уж очень), но на основе Аннотаций можно быстро написать свою ORM, с блекджеком и…
Рекомендую почитать про Doctrine ORM, и причины почему Symfony сделала по дефолту (вместо Prorel) именно Doctrine.
Конечно в реальных проектах Entity Manager у Doctrine ORM очень прожорливый (ну уж очень), но на основе Аннотаций можно быстро написать свою ORM, с блекджеком и…
-2
Я целиком и полностью за дата-мэппер, и юзаю доктрину) Но я все это пишу к тому, что AR по этим аргументам никак не проигрывает DM.
DM на мой взгляд идеологически более правильна, но к сожалению доктрина тяжела и потому думаю не особо распространена.
DM на мой взгляд идеологически более правильна, но к сожалению доктрина тяжела и потому думаю не особо распространена.
+1
Рекомендую почитать про Doctrine ORM, и причины почему Symfony сделала по дефолту (вместо Prorel) именно Doctrine.
Насколько я помню, то тут есть нюансы:
* пропел давным давно как мертв (ок, ещё немнго трепыхается, но не суть)
* это произошло ещё в версии 1.2 симфони, когда была доктрина 1.х с реализацией ActiveRecord.
* доктрина единственная серьезная ОРМ которую можно юзать без привязки к фреймворку.
0
Что мешает использовать аннотации в Active Record? Что мешает в Active Record назвать таблицу репозиторием?
Имхо, вы совсем не понимаете сути data mapper'а. Она не в синтаксическом сахаре, она в отделении данных от логики их сохранения. Более того, она вообще в правильном использовании ООП.
И плюсы её очевидны (но совсем не те, про которые писали вы) — гибкость и разделение ответственности.
Имхо, вы совсем не понимаете сути data mapper'а. Она не в синтаксическом сахаре, она в отделении данных от логики их сохранения. Более того, она вообще в правильном использовании ООП.
И плюсы её очевидны (но совсем не те, про которые писали вы) — гибкость и разделение ответственности.
+1
В Yii в Active Record есть метод, возвращающий имя таблицы, и при желании можно указать префикс, обозначающий коннект, который обеспечивает доступ к источнику
«table» не сериализуется, он же метод
При восстановлении объекта коннект восстанавливается лениво через локатор Yii::app()->db1 только при чтении или записи объекта
При тестировании в локатор можно подсунуть настройки тестовой базы или мок
Я думаю, это достаточно гибко
class User {
function table() { return 'db1.user'; }
}
«table» не сериализуется, он же метод
При восстановлении объекта коннект восстанавливается лениво через локатор Yii::app()->db1 только при чтении или записи объекта
При тестировании в локатор можно подсунуть настройки тестовой базы или мок
Я думаю, это достаточно гибко
0
В книге на самом деле 4 шаблона, ну да ладно… Касательно DataMapper лучше всего добавить в объект поле для хранения ссылки на экземпляр мапера, что позволит использовать просто
Давно мучает небольшой вопрос: а какая библиотека позволяет отслеживать изменение свойств объектов и динамически менять связанные объекты? (см код)
$object->save()
и не задумываться о том как был создан объект (эта связь все равно понадобится если захочется реализовать прозрачную выборку связанных объектов)Давно мучает небольшой вопрос: а какая библиотека позволяет отслеживать изменение свойств объектов и динамически менять связанные объекты? (см код)
// Объект
$object = new MyObject([id = 2, parent_id = 1]);
// Родительский объект (ленивая загрузка связанного объекта)
$parent = $object->parent; // MyObject([id = 1, parent_id = null]);
// Дочерние объекты (ленивая загрузка связанного объекта)
$childs = $parent->childs; // [MyObject([id = 2, parent_id = 1]), MyObject([id = 4, parent_id = 1])]
// "Магия"
$object->parent_id = 3;
// $object->parent === MyObject([id = 3]) - новый родитель
// $parent->childs === [MyObject([id = 4, parent_id = 1])] - объект удалился у предыдущего родителя
$object->parent = new MyObject([id = 3]);
// $object->parent_id === 3
// $parent->childs === [MyObject([id = 4, parent_id = 1])]
0
Вы опять спустились до уровня SQL. ID — это свойство сущности, но вы работаете с объектами. Доктрина позволяет делать так:
В данном случае на место друга встанет прокси объект с единственным определенным свойством — id:3, до тех пор пока вы не запросите какие-то свойства друга — ничего из базы загружено не будет. Вроде как можно даже что-то поменять и опять же грузиться ничего не должно, только обновится при
$user = new User();
$user->setFriend($em->find(3, 'Blabla\User'));
В данном случае на место друга встанет прокси объект с единственным определенным свойством — id:3, до тех пор пока вы не запросите какие-то свойства друга — ничего из базы загружено не будет. Вроде как можно даже что-то поменять и опять же грузиться ничего не должно, только обновится при
$em->flush()
0
Нет, скорее перешел к полноценному ООП: если меняются свойство то это изменение глобально и действует сразу, без всякого
* — понятно что это возможно только если все объекты хранятся в памяти, но если их немного это не проблема.
$em->flush()
и подобных. Т.е. если меняем $object->parent
то в $object->parent->childs
должен сразу добавиться $object
** — понятно что это возможно только если все объекты хранятся в памяти, но если их немного это не проблема.
0
В ООП вы управляете объектами. Представим что вы — объект, человек. От того что я у вас номер на машине поменял — у вас машина тут же не обновилась до соответсвующего номера? Более того невозможно проверить существует ли машина с таким номером без запроса в базу.
ID — это то, что связывает сущность внутри приложения с строкой в таблице в базе. Это нельзя менять вот так вот просто
ID — это то, что связывает сущность внутри приложения с строкой в таблице в базе. Это нельзя менять вот так вот просто
0
Изначально
$object->parent->childs
содержит какой то набор объектов («первый набор»), меняем $object->parent
и… и ничего, $object->parent->childs
по прежнему содержит «первый набор», хотя в реальности этот набор уже изменился — в этом то и проблема (обычно она решается ручным релодом $object->parent->childs
, что неудобно и не логично)0
Ничего не понял, можно в коде пример привести? Вы имеете ввиду что $user->setGroup($group) а потом нужно $group->addUser($user)?
0
Вы имеете ввиду что $user->setGroup($group) а потом нужно $group->addUser($user)?
Именно (только вот это
$group->addUser($user)
должно быть полностью прозрачно).0
Похоже на то что нужно, но немного непонятно: будут ли обновлены объекты до вызова
flush()
в примере с OneToMany
?0
Эта ваша ответственность следить за правильностью ссылок из объектов друг на друга. Если ваш код поддерживает это (т.е в коде метода addUser вы устанавливаете $user группу), то никаких проблем с Doctrine у вас быть не должно.
0
Зачем делать вручную что-то что может быть автоматизировано? ;) И потом, это очень удобно и позволяет полностью забыть о самом мапере и просто использовать нужные объекты со всегда актуальными свойствами.
0
В Doctrine они и так будут актуальными, если вы правильно устанавливаете связи между объектами, как написали вам здесь.
0
Это особенности реализации, а не свойство паттернов. Было бы прекрасно, если бы все можно было вот просто так взять и поменять — проблема в том, что это очень сложно реализовать.
0
Парадигма ООП не вводит понятие аспекта.
В данном случае вы пытаетесь ввести аспект связей(древовидной) объектов и сохранить его целостность.
А это, увы, уже задача бизнес-логики.
В данном случае вы пытаетесь ввести аспект связей(древовидной) объектов и сохранить его целостность.
А это, увы, уже задача бизнес-логики.
0
Возможно, но при наличии данных об этих связях целостность может быть обеспечена автоматически, что гораздо удобнее чем везде обеспечивать её вручную.
0
ООП не про связи объектов, так же как не про здоровье программистов. Это лишь парадигма программирования, в которой чётко указаны термины и теоремы.
0
Скорее не только про (или aгрегация тоже не относится к ООП?)
0
Агрегация — это не связь объектов, это отношения классов.
0
Эти отношения классов, являются способом реализации полиморфизма.
0
Как раз таки связь (один объект вложен в другой).
0
А чем связь отличается от отношения в контексте ООП?
0
Связь — семантические отношения между экземплярами классов, другими словами это уже некие отношения основанные на значении класса для программиста в контексте задачи, а не в контексте теории ООП.
Агрегация, Композиция и Наследование — отношения между объектами/классами в контексте ООП, без учета семантики.
Агрегация, Композиция и Наследование — отношения между объектами/классами в контексте ООП, без учета семантики.
0
public function setParent($parent)
{
$this->parent->removeChild($this);
$parent->addChild($this);
$this->parent = $parent;
}
А у вас не ООП, а каша. С таким уровнем «магии» ваш проект уже завтра станет неподдериваемой шляпой.
0
Встанет таки полный объект, для прокси нужно использовать
em->getReference()
.0
Чем в приведённом вами примере DataMapper отличается от TableGateway?
0
На сколько я понимаю, Table Gateway объект является Data Mapper объектом, но не наоборот.
0
Table Gateway — это частный случай, в данном случае он и есть. Data Mapper — более общая вещь, он мапит на что угодно — совершенно необязательно в целевом хранилище вообще есть понятие таблицы.
0
Не упомянут важная фича DataMapper'a — он тесно привязан к соответствующей сущности, и это увеличивает связанность (coupling), в то время как сущность, сама себя сохраняющая, наоборот, обладает более высокой связностью (cohesion). С другой стороны, мапперы примитивны, лежат в отдельном слое, друг с другом не связаны, и модифицировать их достаточно просто.
0
Недостатки: Вам придется гораздо больше думать, перед тем как написать код.я бы не назвал это недостатком
0
Сейчас почти все AR позволяют легко менять сторадж, за это ответственен DBAL. Даже в примерах их статье видно, что для DM оказывается так же зависим от PDO.
0
Гм. Это всё прекрасно, но вот у меня например возникает такой вопрос: а что думает DataMapper о прозрачной массовой ленивой подгрузке данных для коллекции объектов?
Тут правда вопрос, что об этом думают и существующие AR?
Сам подход — это я у себя обычно применяю, идея его в том, что объект помнит, частью какой коллекции является, и при обращении к геттеру свойства, требующего подгрузки данных из БД, подгружает и прописывает свойство сразу для всех объектов в «своей» коллекции.
Получается, что и писать удобно (не надо явно просить подгрузку свойств), и работает быстро — и лениво, и никогда нет сотни селектов в цикле, отдельных для каждого объекта.
Тут правда вопрос, что об этом думают и существующие AR?
Сам подход — это я у себя обычно применяю, идея его в том, что объект помнит, частью какой коллекции является, и при обращении к геттеру свойства, требующего подгрузки данных из БД, подгружает и прописывает свойство сразу для всех объектов в «своей» коллекции.
Получается, что и писать удобно (не надо явно просить подгрузку свойств), и работает быстро — и лениво, и никогда нет сотни селектов в цикле, отдельных для каждого объекта.
+2
Sign up to leave a comment.
Active Record против Data Mapper-а для сохранения данных