Какие запросы мешают вам использовать ORM?

atv

Новичок
Разговор сильно ушёл в сторону, и я хочу немного вернуться к теме ORM.

Я скажу чего бы я хотел от ORM. Я не хотел бы беспокоиться о синхронизации данных. Ведь объект содержит копию данных, соответственно, изменения в БД никак не отразятся в объекте. Т.е. хотелось бы, чтобы объект был как бы ссылкой на запись БД. Имея на руках только SQL реализовать подобное, мягко говоря, тяжко.

Немного удобнее стало работать с БД после появления курсоров, однако этого недостаточно. Вот если бы в БД были события, связанные с добавлением, изменением и удалением записей, и можно было вешать на них свои обработчики, это в корне изменило бы ситуацию и упростило бы реализацию ORM. Именно эти события я имел ввиду. А то что написал whirlwind, это немного не то.

Как бы я хотел работать с ORM.
PHP:
$customers = new CustomerList();
$orders = new OrderList();
// то что это связанные списки, должно быть известно системе

$customers->setFilter('name = "John"');
// чтобы применение фильтра отразилось бы на списке $orders

$orders-> setFilter('date > 12.9.2005');
// опять же, применение фильтра коснулось списка покупателей,
// а также, чтобы очерёдность применения фильтров не имела значения

foreach($castomers as $cust) {
    $orders = $cust->orders;
}
// чтобы была отложенная загрузка

foreach ($orders as $order) {
    $cust = $order->customer;
}
// чтобы были взаимные ссылки между списками
Хочется, также, чтобы можно было начинать работу с любой сущности, и уже от неё по ссылкам плясать дальше. Например, начали со списка $orders и из него пошли на покупателя $cust = $orders[1]->customer, и дальше если нужно, $cust->city, или обратно на список заказов $cust->orders, но уже для этого пользователя.

Самое интересное, что нынешним ORM приходиться отчасти дублировать БД, им также нужно следить за связями таблиц, не мешало бы следить за целостностью данных, так как это необходимо для генерации событий. И даже события есть в БД - триггеры, только в таком виде они малопригодны.
 

john.brown

просто кулибин
mrjazz

А кто тут говорит о переносе бизне логики в бд? Кроме Sad Spirit, конечно, который тригеры туда массово хочет запихнуть :) Я, помнится, ратовал за объектные бд, что как раз соответствует идеалогии орм... Ибо оперирует не с записями а объектами - т.е. именно тем, что нам так по зарез требуется получить...
 

whirlwind

TDD infected, paranoid
PHP:
$customers = new CustomerList(); 
$orders = new OrderList(); 
// то что это связанные списки, должно быть известно системе 

$customers->setFilter('name = "John"'); 
// чтобы применение фильтра отразилось бы на списке $orders 

$orders-> setFilter('date > 12.9.2005'); 
// опять же, применение фильтра коснулось списка покупателей, 
// а также, чтобы очерёдность применения фильтров не имела значения 

foreach($castomers as $cust) { 
    $orders = $cust->orders; 
} 
// чтобы была отложенная загрузка 

foreach ($orders as $order) { 
    $cust = $order->customer; 
}
Вообще мне так не нравится. С небольшими вариациями так сделано в propel-е. У меня объект - это не коллекция, а единственный экземпляр, но этот экземпляр может находиться в режиме итератора. Класс устойчивого перегружается методами, но на самом деле так гораздо удобнее. Нет необходимости плодить сущности для достижения мелких целей, достаточно выполнить перегрузку метода. Твой вариант у меня выглядел бы так

PHP:
$customers = new Customer(); 
$orders = new Order(); 
// то что это связанные списки, должно быть известно системе 

$customers->filterBy('name',"John"'); 
// чтобы применение фильтра отразилось бы на списке $orders 

$orders-> filterCustom('date > 12.9.2005'); 
// опять же, применение фильтра коснулось списка покупателей, 
// а также, чтобы очерёдность применения фильтров не имела значения 

// Вот так вряд-ли можно, да и выглядит криво - неясно почему // ордеры должны бытьотфильтрованы по кастомеру да и два
// цикла несвязанных
//foreach($castomers as $cust) { 
//    $orders = $cust->orders; 
//} 
// чтобы была отложенная загрузка 
//
//foreach ($orders as $order) { 
//    $cust = $order->customer; 
//}

// вариант с bulk load
$l = new BulkLoader();
$l->attachEssence($orders);
$l->attachEssence($customers);
$l->fetch();
while ( $l->next() ){
    $order = $l->get("Order");
    $customer->$l->get("Customer");
}

// либо
$orders->filterBy("customer",$customers);
$orders->selectObjects();
while ( $orders->next() ){
     $customer = $order->get("customer");
}
Второй вариант работает. Первый у меня пока еще не отработан, но на в приоритетных задачах.

То что ты написал, это действительно не возмлжно в данный момент в реальном времени, да и вряд-ли к этому нужно стремиться, потому что один вызов save или selectObjects написать не трудно. Коммитить изменение каждого атрибута или фильтра - это непозволительная даже на мой взгляд роскошь

-~{}~ 25.09.06 18:42:

ЗЫ. да, кстати, foreach($castomers as $cust) можно будет сделать, но это по настоящему мелочи и при обсуждении общих моментов не имеют особого значения.

-~{}~ 25.09.06 18:46:

И еще только что увидел

>или обратно на список заказов $cust->orders

Ну это круто. Подразумевается что ссылочный атрибут ссылается на экземпляр, а не на коллекцию. Хотя... Неплохая идея - можно добавить новый тип атома коллекция ссылок. Спасибо за идею ;)
 

atv

Новичок
Вообще мне так не нравится... У меня объект - это не коллекция, а единственный экземпляр,
Да, но суть таблицы - это коллекция обектов, а запись - это объект. А если нужно достать два экземпляра из коллекции... Да и для синхронизации данных удобней такая расстановка.

неясно почему ордеры должны бытьотфильтрованы по кастомеру
Потому что это связанные списки. В БД ведь нельзя внести заказ на несуществующего покупателя, значит и в списке не должно быть потеряных данных.

да и вряд-ли к этому нужно стремиться, потому что один вызов save или selectObjects написать не трудно.
Дело в том, что нужно поддерживать ссылочную целостность данных.

Подразумевается что ссылочный атрибут ссылается на экземпляр, а не на коллекцию.
Нет. Имеется ввиду, что при обращении $cust->orders к коллеции $orders автоматом применяется фильтр $oredrs->setFilter('cust_id = :cust_id').
 

whirlwind

TDD infected, paranoid
> Да и для синхронизации данных удобней такая расстановка

где написано, что удобней?

> В БД ведь нельзя внести заказ на несуществующего покупателя, значит и в списке не должно быть потеряных данных

Это не то. Это все ключами и так регулируется. Я имею вввиду контекст. Ты нигде не связываешь экземпляр Customers с экземпляром Orders. С какой стати Orders должен догадаться что ты решил применить к нему именно вот этот конкретный фильтр. Экземпляров может быть сколько угодно.

> Дело в том, что нужно поддерживать ссылочную целостность данных.

Это делает СУБД или бизнес логика _onDelete, _onInsert, etc, если ссылочная целостность не поддерживается СУБД.

>Нет. Имеется ввиду, что при обращении

Т.е. ты предлагаешь делать каждый объект сингельтоном что ли? Подумай хорошенько.

-~{}~ 25.09.06 19:30:

>А если нужно достать два экземпляра из коллекции

Да хоть десять. Объект сам по себе итератор.

PHP:
$order = new Order();
... // filters etc...
$order->selectObjects();
while ( $order->next() ){
     echo "Order: ",$order->asString(),"\n";
     for ( $i = 0; $i < $order->getNumLines(); $i ++ )
          echo "Product: ",$order->getLine($i)->get("product")->asString(),"\n";
}
 

atv

Новичок
где написано, что удобней?
Нате драсьте... Я написал.

Это все ключами и так регулируется.
В БД да, а в нашем объектном отображении?...

Ты нигде не связываешь экземпляр Customers с экземпляром Orders. С какой стати Orders должен догадаться что ты решил применить к нему именно вот этот конкретный фильтр.
Они связаны в БД, значит должны быть связаны и в отображении. А если это не так, то меня такая ORM не устраивает.

Всё что я написал, это мои пожелания к ORM, с которой я стал бы работать. Без этого ORM мне не интересна.
 

whirlwind

TDD infected, paranoid
>Нате драсьте... Я написал.

> Да и для синхронизации...

Т.е. вы, являясь адептом организации связей между устойчивыми объектами, продолжаете настаивать, что якобы для синхронизациии и т.п.? Если вы позиционируете себя как пользователя ORM, то может не стоит делать таких глубокоидущих заключений?

>>В БД да, а в нашем объектном отображении
>Это делает СУБД или бизнес логика _onDelete, _onInsert, etc, если ссылочная целостность не поддерживается СУБД

> А если это не так, то меня такая ORM не устраивает

atv а если без фатализма? Я же не впариваю тебе свой ORM за деньги. У нас диалог, по этому я просто прошу - подумай еще раз, будет ли тебе действительно удобно использовать такие классы. Сможешь ли ты перегружать методы, писать собственные для таких классов и т.п. Сможешь ли ты использовать реализованные сингельтонами критерии в хотя бы в двух разных функциях/методах. Просто возьми мой фреймворк и попробуй, а потом скажи - да, это полнейший ацтой, который добавил мне гемороя в процессе разработки. В который раз повторяю - ORM это не способ извлечения данных. Просто взгляни на ситуацию отталкиваясь не от мертвых таблиц, а от активных объектов в которых закодирована бизнес логика. Изначально у тебя есть объекты. Все остальное - вторично. Иначе ничего не могу сказать, кроме того, что все твои хотелки высосаны из пальца.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Автор оригинала: bkonst
И как веб-пользователь увидит код Web-приложения? (Приступ острого кретинизма у администратора не считаем, так как в этом случае администраторский доступ уйдет с той же легкостью).
Я имел в виду не веб-пользователя и не злоумышленника, а всего лишь разработчика, имеющего, естественно, доступ к БД и не имеющего понятия обо всей красоте и строгости бизнес логики. А ну как он через phpWhateverAdmin --- из лучших побуждений --- решит что-нибудь исправить?

Автор оригинала: john.brown
А кто тут говорит о переносе бизне логики в бд? Кроме Sad Spirit, конечно, который тригеры туда массово хочет запихнуть
Вас так пугают триггеры? Вы хотите поговорить об этом?

Место части бизнес-логики, связанной с целостностью и непротиворечивостью хранимых данных, таки да, в базе. Потому что "реализация ссылочной целостности в ORM" фактически означает, что никакой ссылочной целостности у вас не будет вообще.
 

atv

Новичок
Иначе ничего не могу сказать, кроме того, что все твои хотелки высосаны из пальца.
Моя твоя не понимай. Я уже сказал, что мне удобно не думать о БД. Представь, что у тебя нет БД. Есть только набор объектов, организованных в коллекции и имеющих взаимные ссылки друг на друга. Вот это я называю прозрачностью. Возможности ORM в части генерации запросов меня не интересуют. А бизнес логика никуда не денеться.

Будет ли мне удобно? А почём я знаю, на первый взгляд удобно, а на практике не проверял. Возможно вылезут несоответсвия в моих пожеланиях, возможно реализация окажеться непрактичной и т.д. и т.п.

Зато могу сказать, что ORM в нынешнем виде мне не удобны. Ну и бог со мной, не надо воспринимать так близко к сердцу моё отношение к ORM.
 

dr-sm

Новичок
Автор оригинала: atv
Я уже сказал, что мне удобно не думать о БД. Представь, что у тебя нет БД. Есть только набор объектов, организованных в коллекции и имеющих взаимные ссылки друг на друга. Вот это я называю прозрачностью. Возможности ORM в части генерации запросов меня не интересуют. А бизнес логика никуда не денеться.
То что ты описал, насколько я знаю, называется Inversion of Control Pattern (IoC), и замечательно реализовано в Spring Framework в Java. Но это немного не по теме :), тк там все настолько прозрачно, что для конечного пользователя практически нет никакой разницы используется ли в качестве Persistence Layer какой-то ORM или просто JDBC(SQL). Однако, ввиду моего не большого опыта работы в данной области, не берусь судить подходит ли это для PHP. Возможно, если все это реализовать, в результате получится не совсем (совсем не) lightweight решение.
 

bkonst

.. хочется странного?...
atv
[...]Как бы я хотел работать с ORM.[...]
Интересный подход.... но как работать с несколькими разными наборами (допустим, "старых" клиентов и клиентов с суммой заказов больше N) одновременно, если списки будут связываться автоматически?

Sad Spirit
Я имел в виду не веб-пользователя и не злоумышленника, а всего лишь разработчика, имеющего, естественно, доступ к БД и не имеющего понятия обо всей красоте и строгости бизнес логики. А ну как он через phpWhateverAdmin --- из лучших побуждений --- решит что-нибудь исправить?
А как он --- из лучших побуждений --- решит что-нибудь "исправить" просто через бизнес-логику?
Или просто подойти к системному администратору, стукнуть его по голове и выкинуть сервер из окна?
Если разработчик настолько невменяем, зачем вообще давать ему доступ к рабочей БД? Пусть сидит в отдельной песочнице и проверяет результаты своих действий там. А вменяемый пусть забирает наработки и включает их в основную ветку.

Место части бизнес-логики, связанной с целостностью и непротиворечивостью хранимых данных, таки да, в базе. Потому что "реализация ссылочной целостности в ORM" фактически означает, что никакой ссылочной целостности у вас не будет вообще.
Заниматься (проверять) ссылочную целостность должна база, да. Однако "противоречивость" хранимых данных - это понятие сильно растяжимое. Скажем, несходящийся годовой баланс - это ведь противоречивые данные?
 

atv

Новичок
То что ты описал, насколько я знаю, называется Inversion of Control Pattern (IoC)
Вот есть статьтя по IoC http://wiki.agiledev.ru/doku.php?id=ooad:dependency_injection. Разве там об этом.

но как работать с несколькими разными наборами (допустим, "старых" клиентов и клиентов с суммой заказов больше N) одновременно, если списки будут связываться автоматически?
Применил фильтр, забрал итератор (getIterator()), и можно применять следующий фильтр.
 

john.brown

просто кулибин
Sad Spirit
Вас так пугают триггеры? Вы хотите поговорить об этом?
Нет, меня пугает то, что рано или поздно может понадобиться поправить что в бизнес логике, или на другую бд переехать. А не дай бог, поправлять придется чужой скрипт, изобилующий триггерами в бд... Это будет не самое приятное и плодотворное занятие...
Как я уже говорил, радикальное решение проблемы - это оъектная бд, но, за их не имением, каждый волен решать сей вопрос по своему. Лично меня больше устраивает орм, чем мистические триггеры и прочие чудеса в бд...
 

dr-sm

Новичок
Автор оригинала: atv
Вот есть статьтя по IoC http://wiki.agiledev.ru/doku.php?id=ooad:dependency_injection. Разве там об этом.
ну да вполне, если рассмотреть в нужном контексте :). Получается что все механизмы persistence будут спрятаны
внутри IoC контейнера, а наружу торчать будет:
...набор объектов, организованных в коллекции и имеющих взаимные ссылки друг на друга.
 

whirlwind

TDD infected, paranoid
>Моя твоя не понимай. Я уже сказал, что мне удобно не думать о БД. Представь, что у тебя нет БД. Есть только набор объектов, организованных в коллекции и имеющих взаимные ссылки друг на друга. Вот это я называю прозрачностью.

Покажи где в одном из примеров кода, которые я привел здесь, хотя бы один запрос? Ты видишь в примерах, где один объект получается по ссылке из другого? Здесь еще не было карт, посредством которых реализуются связи многие ко многим, но они есть. Моя твоя не понимай. Вроде это вы начали плясать от запросов. Одно радует - мне наконец-то удалось донести, что ORM это объекты, а не запросы.
 

mrjazz

Новичок
Автор оригинала: john.brown

А кто тут говорит о переносе бизне логики в бд? Кроме Sad Spirit, конечно, который тригеры туда массово хочет запихнуть :) Я, помнится, ратовал за объектные бд, что как раз соответствует идеалогии орм... Ибо оперирует не с записями а объектами - т.е. именно тем, что нам так по зарез требуется получить...
Да но как быть с onBefore*, onAfter* обытиями? база это всего-навсего база, и ты не отправишь из неё мейл, не совершишь манипуляций с файлами и много чего еще не сделаешь, что вполне может лечь на плечи модели. А ведь ОРМ есть не модель в чистом виде, но её близкий родственник. Поэтому я за реализацию бизнес-логики в коде.
 

john.brown

просто кулибин
и ты не отправишь из неё мейл
Вот чем не должна заниматься модель, так это отправкой мейлов ;) Да и манипуляции файлами, кроме особых случаев, весьма сомнительны... Для этого есть контроллер, который решает, что и когда отпралять.
ОРМ есть не модель в чистом виде
Орм есть прослойка между бд и твоей объектной моделью, позволяющая незаметно преобразовать строку, полученную из реляционной бд в объекты модели. В случае же объектной бд, необходимость в этой прослойке отпадает, т.к. ты сразу из бд получаеш полностью функциональные объекты своей модели.
Поэтому я за реализацию бизнес-логики в коде
Так а кто же против? Реализовываеш свою логику в объектах, и сохраняеш их в бд...
 

whirlwind

TDD infected, paranoid
> Для этого есть контроллер, который решает, что и когда отпралять.

Контроллер - это кусок паттерна. Я могу применять этот паттерн, а могу и не применять. Чем плохо например отправлять емейл непосредственно из документа, который фиксирует поступление денег в систему? Не важно является ли создание этого документа результатом заполнения пользователем формы на сайте или же это начисление бонуса, которое выполнилось автоматически - пользователь всегда получит сообщение о фактическом пополнении баланса. Хотя это один из немногих способов контроля подобной ситуации, по каким причинам этот вариант менее корректен чем другие?
 

john.brown

просто кулибин
по каким причинам этот вариант менее корректен чем другие?
В основном, моим убеждением, что модель не должна делать несвойственные ей вещи. Это то же самое, что курица сама включает духовку после того, как ее туда поместили ;)
В твоем примере, мне бы понравилось решение, что некий обсервер фиксирует поступление денег, и извещает об этом контроллер. Ибо, возможны варианты, что мейл надо отправлять не всегда, или в разных вариантах поступления надо отправлять по разным адресам или разное количество мейлов. И всю эту логику начнем запихивать в документ?
 

whirlwind

TDD infected, paranoid
> В твоем примере, мне бы понравилось решение, что некий обсервер фиксирует поступление денег, и извещает об этом контроллер. Ибо, возможны варианты, что мейл надо отправлять не всегда, или в разных вариантах поступления надо отправлять по разным адресам или разное количество мейлов.

Я сказал что это один из вариантов. Обсервер подразумевался, но это более тяжелое решение, использование которого не всегда оправдано. Как тут правильно было подмечено - не стоит плодить бессмысленные абстракции. Если нам нужно установить псевдоним единственного атрибута, то нафига заморачиваться порождением новой сущности, такой как таблица псевдонимов, если гораздо проще перегрузить метод getFieldName($attr) непосредственно класса устойчивого объекта?

> И всю эту логику начнем запихивать в документ?

Ну а какая разница? Можно АОП, можно обсервер, почему нельзя непосредственно из документа? Проблема может быть решена множеством способов. Если отправка мыла это всего одна строка, то почему нет? В этом весь цимус ORM. Бизнес логика опирается на события а обработка событий не отдалена от контекста, в котором событие имеет смысл.

Рассмотрим другую ситуацию, без имейлов, раз такое предвзятое к ним отношение. Например, любой документ отражает свое влияние на систему через другие объекты модели. Такой подход применяется для того, что бы обобщить представленные различными классами сущностей данные в виде, удобном для обработки из любого места системы. Абстрактный документ (а этих классов может быть неограниченное количество) использует дополнительные механизмы, такие как регистры оперативного учета, которые требуют указания объектов аналитики (пользователь, продукт, адверт) и ресурса (деньги, количество). Такие регистры - быстрый способ получения информации, отвечающей на вопросы - кто, когда, кому, сколько в самых произвольных комбинациях аналитических срезов. По твоему это все то же должно выноситься в контроллер? А что тогда останется в модели? Если только контроль ссылочной целостности. Но как, если модель настолько бедна бизнес логикой, что не позволяет сделать выводы по поводу зависимостей? Везде надо знать меру. Цель ORM не в том, что бы соблюсти заветы прогрессивных методик разработки, а в том, что бы удобно разложить бизнес логику по полочкам.

-~{}~ 27.09.06 15:20:

PS.

> Это то же самое, что курица сама включает духовку после того, как ее туда поместили

Не совсем правильно. В данном случае - курица знает когда и куда откладывать яйца. А механизм духовки (есть он или нет) не рассматривался.
 
Сверху