MCV – добавление еще одного слоя

MCV – добавление еще одного слоя

При разработке приложений среднего и выше среднего уровня сложностей для реализации модели зачастую используют модель предметной области. При использовании РСУБД это влечет за собой необходимость создания прослойки между базой данных и конечными классами модели, которую реализуют в виде некоторого гетвея, ORM etc. Преимущества такого подхода всем известны, думаю, известны и недостатки. Самая большая проблема, с которой сталкиваюсь лично я - невозможность эффективного взаимодействия представления с моделью в том случае, когда мне нужно получить и отобразить данные. Тривиальный случай - выборка с 3-4 джоинами из таблиц, которые "представлены" различными классами модели. Нетривиальный – вложенные запросы, запросы с объединениями и т.д. Даже ORM, насколько я могу судить после изучения всей русскоязычной литературы, представленной в сети, не умеет решать подобных задач, а OQL еще не настолько гибок и селен, чтоб конкурировать с SQL. Мне попадались решения, в которых использовались разнообразные дополнительные слои и классы, однако, затраты на их реализацию чрезмерно высоки, а рациональность использования - сомнительна.

Я склоняюсь к выводу, что сейчас не существует средств, которые способны заменить "raw SQL|. Как я уже говорил выше, чаще всего с проблемой выполнения сложных запросов приходится сталкиваться решая задачи отображения данных (выполнение select запросов). Почему бы не поступить следующим образом: создать дополнительный слой, содержащий классы с методами, выполняющими сложные запросы и возвращающие данные контроллеру или сразу же в представление в удобном формате (коллекция, массив ссылок на хеш и т.д.). Научив классы сортировать, лимитировать и разделять на страницы полученные данные мы получим довольно сильный механизм. Конечно, он имеет свои недостатки, например, необходимость дублирования преобразования и форматирования полей, иногда приходится повторять некоторые вещи, реализованные в классах модели. Но по сравнению с тем, что приходится выдумывать в рамках стандартной концепции – это ерунда.

Что скажете по этому поводу? Как вы решаете подобные проблемы? Может уже существуют стандартные решения?
 

Demiurg

Guest
SQL сам по себе является абстракцией, и запихивая его в объктную модель любого другого языка мы сильно урезаем sql в функциональности.
 
А в обратном случае мы лишаемся многих преимуществ использования объектной модели. Насколько я знаю, решения с использованием гетвеев или объектно-реляционного отображения (в любых, пусть простых его проявлениях) очень распространены.

-~{}~ 27.11.04 00:13:

Мой пример - это один из способов _вообще_ не урезать функциональность SQL там, где она нужна, а именно в select запросах. СUD-запросы, зачастую являются простыми.
 

Макс

Старожил PHPClub
ИМХО
если работаешь с множеством записей, полученых из SELECT-запроса то лучше использовать Модуль Таблицы (у Фаулера это страница 148 :))
 
Если работать НЕ с выводом т.е. манипулировать объектами и взаимодействовать с контроллером, гораздо удобнее использовать модель предметной области + маппинг или гетвей к БД. Проблемы начинают появляться, как я уже писал выше, только при выполнении сложных select запросов, результаты которых необходимо передавать в представление. Даже используя модуль таблиц не всегда есть возможность не прибегая к чистому SQL выполнять сложные запросы.

-~{}~ 27.11.04 12:47:

В этой теме http://phpclub.ru/talk/showthread.php?s=&threadid=58598&rand=16 я описал класс, который можно применять в обсуждаемом дополнительном слое.
 

crocodile2u

http://vbolshov.org.ru
Не так давно я тоже увлекся концепцией ORM, довольно много времени провел на сайте проекта Propel, почитал кое-что из доступных материалов (правда, нисколько не претендую на всестороннее изучение этих вопросов).

Почитав о Propel, решил сделать свой пакет, позаимствовав многое из того, что прочитал. В настоящее время можно сказать, что "моё осуществление ORM на PHP" готово приблизительно наполовину.

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

Я попробовал применить свои наработки в действующем проекте, и столкнулся с тем, что мой подход по большому счету не приносит тех плодов, которых я от него ожидал. Я, собственно, делал его с тем, чтобы избавиться от "рутинных" SQL-запросов в коде, ну и еще, чтобы обеспечить кой-какие связи между данными, которые MySQL обеспечить не в состоянии - ну например, удаление объектов, связанных с "главным" при удалении последнего, т. е. упростить себе жизнь.

На деле вышло, что код с применением "моего ORM" стал значительно менее понятным, фактически, думаю, разобраться в нем с ходу смогу только я один :). Кроме того, этот подход стал меня в какой-то мере связывать - либо приходится опять-таки писать запросы вручную, либо давать навороченные инструкции "прослойке" ORM.

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

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

В данный момент я все-таки склоняюсь к тому, чтобы отказаться от использования ORM в пользу обычного SQL.

PS: Когда я заинтересовался ORM, я связался с Fisher'ом (, который читал доклад на эту тему на майской конференции), и мы сошлись во мнении, что ORM вовсе не является какой-то панацеей, и, возможно, годится для небольших проектов, но там, где большие объемы данных со сложными связями, где нужна серьезная оптимизация обращений к базе, скорее всего, этот подход малоприменим.
 
Я тоже начал увлекаться ORM сравнительно недавно, после ознакомления с некоторым англоязычными ресурсами и докладом Fisher'а, опубликованным в одном из номеров PHP!nsideю Изучил orm.net.ru, все ссылки, которые он содержит, также ознакомился с наиболее интересными решениями под Perl и PHP. Применять на практике не пробовал, но "мысленно" перепрограммировав один из своих проектов, пришел к неутешительному выводу, что для систем, содержащих таблицы со сложными связями, данное решение не подходит.

Тем ни менее, я активно использую маппинг для тех классов модели, экземпляры объектов которых представляют одну запись в таблице, не имеющей внешних связей. Есть некоторый класс (Object Data Base), в котором реализованы CRUD методы и который наследуют классы модели. Для описания таблиц и настроек для каждого класса использую XML схемы. В тех случаях, когда данного решения недостаточно (а это все случаи, кроме тривиальных), создается гетвей, инкапсулирующий в себе всю логику работы с БД. Затем в самом классе модели используется API, предоставленным этим гетвеем (для каждого класса свой гетвей).

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

crocodile2u

http://vbolshov.org.ru
Собственно, я в некотором роде добился того, что мой пакет классов (всего 5 штук) способен строить довольно сложные запросы с использованием JOIN'ов и т. д.

За основу я взял идею критериев выборки, которую взял из проекта Propel.

В принципе все, что нужно, выбирается, со всеми связями, которые укажешь для выборки. Мне не понравилось то, что вместо удобства кодирования я получил его усложнение, связи между таблицами, которые очевидны в SQL-запросе, становятся совсем непрозрачными, когда в коде появляются вызовы методов, отвечающих за добавление к выборке внешних ключей, критериев и тому подобного добра.
 

pachanga

Новичок
Но, хотелось бы вернутся к основной теме, а именно рациональности создания отдельного слоя, в котором содержатся классы, предоставляющие методы, которые могут быть использованы исключительно в представлении.
Прослойка таких Datasource'ов, которые инкапсулируют специализированные SQL SELECT запросы имеет смысл.

Собственно, зачем нам необходимо полностью загружать доменные объекты, если всего лишь требуется отобразить некоторую информацию во View?
 
Автор оригинала: pacha
Собственно, зачем нам необходимо полностью загружать доменные объекты, если всего лишь требуется отобразить некоторую информацию во View?
Я пришел к такому же выводу. Кроме того, дело не только в загрузке объектов, но и в простоте и гибкости чистого SQL там, где он действительно нужен.

Если кто-то уже занимался реализацией подобных прослоек, поделитесь, пожалуйста, опытом. В данный момент у меня каждый класс прослойки (я их называю классами отображения) представляет один или несколько классов домена. Методы классов "умеют" работать с сортировкой, лимитированием, постраничным отображением, кроме того, внедрен механизм обработки ошибок и поведения, в случае если результат выборки оказался нулевым.

Метод отображения может быть вызван непосредственно из шаблона (в качестве параметра ему передается имя другого шаблона, в который он "обернет" полученные из БД данные), либо из контроллера. В последнем случае, метод отображения просто возвращает данные в какой-то структуре (например, массив ссылок на строки таблицы), а контролер регистрирует их как такие, что могут быть доступны из шаблона. Таким образом, это позволяет неоднократно использовать одни и те же методы отображения для выборки различных данных (порций данных).

Последний абзац относится к тем системам, в которых для реализации представления используются шаблонные движки (смарти, ТТ и т.д), либо продукты вроде масона.
 

syfisher

TDD infected!!
Мы стараемся поступать следующим образом. У нас есть прослойка (или пакет) так называемых Finders. Эти классы знают, каким образом получать данные из базы. Все запросы, любой сложности инкапсулируются в них. Этот пакет эквивалентен твоей прослойки, которая знает о постраничной выборе, сортировке и т.д.

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

Есть также пакет Datasource, который тоже работает с Finders. Datasource используется только во View для того, чтобы отображать RecordSets без конструирования объектов - для оптимизации. Datasource у нас может объединять результаты нескольких Finders (например, применять права доступа, делать ограничения на ветки дерева и т.д.).

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

Все эти шаблоны можно найти в следующих книгах: Domain Drive n Design by Evans, Patterns of Enterprise Application Architecture by Fowler (уже кажется переведена).
 
У нас почти одинаковые подходы, но все-таки различия есть. Прослойку (классы отображения) я использую только в представлении, и они не используются доменными классами. Насколько я понял, у вас класс Finders выполняет еще роль гетвея между DBAL и классами модели. Мои классы отображения можно сравнить с вашим Datasource, но в отличие от него, я не использую гетвеи - только raw SQL.

Касательно написания классов руками. Сейчас я использую систему, основанную на MCV model 2. Контролер представлен контроллером запросов, а также набором классов-команд (действий). У struts позаимствовал классы request (парсит параметры, определяет действие и т.д.) и response (устанавливает заголовки, плюшки, контент). Все запросы перенаправляются на главный обработчик, который на основе конфига (xml) создает и процессит необходимый класс-команду. Сейчас у меня существует 7 стандартных классов команд (отображение шаблона, удаление, редактирование, добавление, перенаправление и т.д.), который могут быть использованы в паре с практически любым классом модели. В данный момент очень редко приходится писать что-то кроме классов модели, но и те, в большинстве случаев, создаются на основе простого маппера и конфига.

-~{}~ 29.11.04 17:03:

Domain Drive n Design - а что это за книга? На амазоне не нашел %(
 

syfisher

TDD infected!!
Автор оригинала: 2NetFly
Насколько я понял, у вас класс Finders выполняет еще роль гетвея между DBAL и классами модели.
Классы модели ничего не знают о Finders, только DataMappers, через которых классы модели записываются и считываются из базы.

В остальном - почти все также, включая контроллеры, команды и т.д.
 

Alexandre

PHPПенсионер
у меня так-же существует некоторая прослойка в эМВиСи, которую я обозвал DataSource.

я пока не так далеко продвинулся в реализации и этот пунктик еще продумываю.

Как я уже описывал в своем топике....http://phpclub.ru/talk/showthread.php?s=&threadid=55126&perpage=20&highlight=mvc&pagenumber=8

данная абстракция у меня работает на уровне двух таблиц Master - Detail.

В случае - а это бывает чаще всего - я использую метод ExecSQL()
 

wrapper

Guest
2NetFly
имхо касательно PHP нет смысла вообще говорить о каком либо варианте OQL
На мой взгляд наиболее подходящий для портирования вариант это Ibatis SQLMAP (www.ibatis.com)
Это нечто среднне между raw sql и маппингом, позволяющее использовать всю мощь SQL но в тоже время избавляющее от множества рутинной работы

syfisher
>Классы модели ничего не знают о Finders, только DataMappers, >через которых классы модели записываются и считываются из >базы.

как бы о DataMappers-ах им знать совершенно незачем
 

syfisher

TDD infected!!
>Классы модели ничего не знают о Finders, только DataMappers, >через которых классы модели записываются и считываются из >базы.

как бы о DataMappers-ах им знать совершенно незачем
Я это и имел ввиду. Только DataMappers знают о Finders. Доменные классы вообще про базу данных ничего знать не должны.
 
Сверху