Pull to refresh

Comments 51

Спасибо, я читал.
Relationship облегчает только получение данных, записать полученные данные в свойство объекта по-прежнему будет не возможно.
хорошо что вам все понятно. Если бы был встречный вопрос, я бы написал как ПРАВИЛЬНО это делать, а так не хочется тратить время. Удачи.
Напишите плиз! Мне прям интересно стало сравнить, как это решается в зенде. Ведь в других yii, kohana, doctrine все елементарно)
в зенде тоже все элементарно. Сводится все к вызову getBlaBlaBla и готово. Главное прочесть доку :)

Сорри, промазал с комментарием. Большой комментарий снизу предназначается вам.

Магический метод getBlaBlaBla который действительно можно вызвать в представлении, если предопределены связи, является вызовом метода модели в представлении. По моему мнению вызывать методы модели в представлении плохо, все данные должны извлекаться в контроллере и передаваться в представление.
Row это обертка для конкретного объекта/записи в базе. Это уже не модель.

Ничего нет плохого если во view будет что-то типа такого

Автор — <?=$this->post->getAuthor()->userName?>

Понятно что будет вызов к БД, но например ОРМ фреймворки так и работают. Не думаю что active records в этом отличается.

согласно MVC, представление вполне может брать данные из модели напрямую.
Плохо? С чего вы взяли что это плохо. Это иногда гораздо удобнее нежели дублирование функциональности в контроллере, а после и во view. Да даже посмотрите на схемку MVC — видите там стрелочку от прямоугольничка View к прямоугольничку Model — так вот это как раз использование модели во view, что бы дать ей представление. А не напихать туда новый порции информации — пусть этим контроллер занимается.

Приведу пример — мне нужно для какой-то странички сделать несколько разных представлений. Допустим три. Допустим возьмем пример из статьи. Если тащат обычными способами — даем статью с комментами, автором, тегами, блекджеком и еще чем-нибудь, по RSS — только статью и именем автора. Я могу спокойно сделать так, что бы от смены лайаута у меня грузились совершенно различные данные, никак не затрагивая при этом контроллер, который будет заниматься совершенно другой работой — к примеру проверит права доступа или еще чего — но это если у нас используется ORM который ограждает нас от глупостей вроде написания SQL запросов в этом месте…
Я вот плотно с Zend не работал. Но в других фреймворках вручную доставать комменты и не нужно. Объявлением relations вы показываете фреймворку как доставать данные из зависимых таблиц.
Да и вы думаете что до сих пор существовал бы такой минус? Zend серьезный фреймворк, и таких проколов там не может быть. Ваш баг — это стандартная задача при разработке (достать зависимые данные), по этому там это решается иначе, 100%. Удачи!
В Yii действительно все выглядит намного проще. Поэтому меня и интересует этот вопрос по отношению к ZF.
Ну я мельком посмотрел — там предлагается получать данные через функцию, для каждого объекта.
А вообще я вам рекомендую попробовать Doctrine — все очень функционально и удобно. А можно сразу в связке с Symfony, тогда вообще бомба.
Конечно, уходить на другой фреймворк, если есть проблема — не выход. Но для себя давно решил, что Zend — это набор хороших, даже отличных классов для чего угодно, но это не фреймворк, слишком много нужно доводить вручную.
> но это не фреймворк, слишком много нужно доводить вручную.
0_o
Фреймворк != полная автоматизация.
А я где то написал что нужна полная автоматизация? Вы использовали Yii или Symfony?
Сравните их с Зендом. Там все более родственно можно так сказать. В Зенде классы почти независимые, что есть и плюсом, и минусом одновременно.
Несогласны?
Если минусуете, аргументируйте, пожалуйста, свою позицию.
Не минусую. Вы сказали что зенд это не фреймворк, я с этим не согласился, все остальное это совершенно о другом.
Я не согласен. Фреймворки это достаточно широкое словечко.

Да и архитектура фреймворка вместе с идиологией можут быть какоми угодно, в ZF мне ничего не навязывает, но если я буду использовать встроенные компоненты — он будет собираться по кубикам и подходить друг к другу не хуже нежели symfony — да мне придется потратить чуть больше сил, но зато мне не придется рвать волосы на жопе, что бы изменить поведение фреймворка потому что захотелось такого вот поведения а оно… как бы это сказать… слегка не в стиле получаеться.

А сравнивать ZF и Symfony в качестве двух равновесных фреймворков я бы не стал. По крайне мере не встречал, что бы компоненты из других фреймворков использовались так же часто в своих велосипедах, прикрученные к другим фреймворкам и многие разработчики считали что это ничем не хуже нежели использование стандартных решений встроенных в язык.

Но плата за это — кривожопые реализации, неоптимальное использование ресурсовов, отсутствие нормального ядра, которое бы действительно объеднило это все в полноценную фреймворк в вашем понимании (все связанно, работает вместе, отлаженно и ничего лишнего делать не надо). То что есть сейчас в Zend_Application — это нецензурный зверек на костылях.

зы
в этом комменте слишком много раз повторяется слово фреймворк…
Вот с вами я согласен. И я думаю мы прийдем к выводу, что из коробки Zend удобен точно не будет. Ясно, что каждый нормальный разработчик все настроит под себя, у него будет уже свой каркас на основе Zenda. Но такой связанности как Symfony+Doctrine он врятли получит.

И Zend, понимая такую ситуацию начал делать Zend_Application, Zend_CodeGenerator, Zend_Tool_*

P.S. Я ни в коем случае не обижаю Зенд. Очень класнючий фреймворк, чего только стоит Zend_Search_Lucene! Просто у меня не срослось с ним)
Мне к примеру раньше был удобен. Сейчас я стал более ленив и скорее всего, если потребуется поднять стандартный проект что бы там все было стандартно — возьму как раз связку симфонию + доктрину.

Работая с проектом где мне будет необходима дополнительная гибкость + еще кучу разных неизвестных плюшек — то увы, но в руки возьму зенд.

Ясно, что каждый нормальный разработчик все настроит под себя, у него будет уже свой каркас на основе Zenda.
Он получит приложение. Посмотрите в сторону Zend_Tool и Zend_Application — используя их уже можно получить большую связанность и «стандартность» архитектуры приложения.

А Zend_CodeGenerator — немного для другого. Я вот его сейчас активно использую для генерации модели.
Дело в том что я задавал этот вопрос ранее на askdev.ru ( www.askdev.ru/question/627/Zend_Db_Table-Relationships-%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8-%D0%BF%D0%BE%D0%B4%D1%86%D0%B5%D0%BF%D0%BB%D1%8F%D1%82%D1%8C-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BA-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%83/ ).
Где вы (я полагаю это вы) в итоге согласились с представленным выше решением.

Безусловно мне будет все еще интересно услышать правильное решение с использованием relationships, если оно у вас есть.

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

Раз уже на то пошло, постараюсь детально изложить свои мысли вечером и напишу ответ на аскдеве.

ps — на минусы не обращаю внимания, они на то и минусы :)
protected $_rowClass = 'Default_Model_DbRow_News'; — определили Row класс в классе таблицы.

В классе Default_Model_DbRow_News объявляем переменную комментариев.

public $comments = null;

Теперь когда в цикле вы будете делать

$record->comments=$comments;

Все будет работать.

Т.е всего что нужно сделать это объявить переменную, тогда ругаться не будет.

Это немного упростит ваш хак.
Спасибо.

Но, думаю вы согласитесь, ваш пример хорош только для одноразового использования.
Таких случаев как News и Comments может быть множество. Прийдется для каждого создавать свой Row класс. Заранее продумывать какие свойства в нем объявлять.
Я думаю что не стоит менять поведение самого зенда и добавлять что-то в внутренние массивы типа _data.

Если вам нужно что-то многоразовое вспомните о ООП и наследовании. Сделайте базовый Row Класс и наследуйте те у которых будут комментарии от него.

Код не должен быть через-чур гибкий и делать все динамически. Нельзя этим злоупотреблять.
foreach($news as $record)
{
  $comments=$Comments->fetchAll(“news_id=”.$record->news_id);
  $record->newDataProperty(‘comments’);
  $record->comments=$comments;
}


* This source code was highlighted with Source Code Highlighter.

Это просто жесть. Зачем делать запросы к базе в цикле? Если уж на то пошло, то соберите все идентификаторы в массив. Потом выборку по in (1, 5, 6, 7) + order, и в цикле разберёте по новостям. Будет 2 запроса вместо фиг знает скольки.

И лучше не часто использовать Zend_Db_Table_Row(set) для работы с базой. Если задача просто вывести новости, то никакой проблемы заметно не будет. А вот при повсевместном использовании это сильно затормозит работу приложения. Проверено на личном опыте =).
ну тут проблема неоднозначная. Используя ORM мы получаем удобство кодирования, скорость разработки но взамен получаем меньше контроля над запросами и в итоге нагрузка на БД.
Active Records стоит где-то посредине между plain sql и ORM.

Если проект будет большой то лучше использовать ORM/Active Records и позаботиться о масштабируемости другими средствами.
Согласен. Но и ORM надо использовать с умом, а не как предлагает автор, запрашивая базу при каждой итерации цикла.

Что же касается Zend Framework, то наиболее удобным и гибким методом для извлечени данных считаю Zend_Db_Select. Создав более или менее абстрактный запрос к основным таблицам модели, в будущем его можно без проблем модифицировать в специализированных методах.
Всетаки опровергну, цель заметки не в агитации за множественные запросы к базе данных.
Данная структура базы данных служит лишь примером, то же можно сказать и по отношению к коду.
Хорошо, что вы это понимаете. Работая с чужим кодом насмотрелся подобного рода выборок данных предостаточно, поэтому отношусь к ним с подозрением.
Долго смотрел в сторону ZF-ной модели… и увидел многое. Во первых — селект ихний хоть и хорош, но давайте спустимся с высоты ооп и посмотрим все глазами адекватных людей — это большой такой костыль для SQL, где половину всего я пишу плейн текстом и основное предназначение которое — облегчить динамическую сборку запросов.

И если вам нравится ZF Select, то вообще советую посмотреть в сторону Doctrine с ее DQL, который хоть и чуточку более плейн текстовый, но способен на достаточно больших запросах не превращаться в треш который невозможно понять с превого, второго и третьего раза и умеет возвращать данные кучкой объектов с которыми можно работать
>Во первых — селект ихний хоть и хорош, но давайте спустимся с высоты ооп и посмотрим все глазами адекватных людей — это большой такой костыль для SQL
Это так, но такой «костыль» помогает вести командную разработку, т.к. динамическая сборка запроса облегчается в разы без боязни что-то испортить в исходном варианте.

>И если вам нравится ZF Select, то вообще советую посмотреть в сторону Doctrine с ее DQL
Честно говоря, давно собираюсь присмотрется поближе, т.к. паттерн Row, Rowset совсем не вызывает симпатии.
Первое — почему это ActiveRecord стоит где-то посредине plain sql и ORM. AR — это вполне себе такая хорошая реализация ORM, которая будет получше нежели Table/Row Gateway с намеками на AR которая используется в ZF.

Используя ORM мы получаем удобство кодирования, скорость разработки но взамен получаем меньше контроля над запросами и в итоге нагрузка на БД.

По поводу контроля запроса категорически не согласен. Давайте к примеру взглянем на такую замечательную чтуку как DataMapper, которая позволяет нам сделать такую замечательную вещь как пограничный слой между объектами используемыми в приложении и хранилищем. И в этом слое мы, при должном желании может делать все что нам угодно (хотя кстати встречал людей, которые советовали и в этом слое «избавляться» от SQL...).

К примеру у нас есть ситуация (из коммента выше но чуть усложненная) — когда нам следует вытаскивать несколько статей, несколько полей из юзера (к примеру имя, репутация), и список тегов к этой статье. Начинаем вытаскивать в запросе и понимаем, что наступает небольшая такая жопа. И вот тут нам следует на начинать вопить какие плохие ORM-ы, а подумать головой и к примеру вспомнить самый простой и очень действенный способ в данном случае — создаем еще один метод, Назовем его getArticlesWithComments(). Где сделать три-четыре толстых запроса к бд, которая прожует их в несколько раз лучше и собрать все в замечательные объекты.

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

Довести бы его до состояния на котором он тут лежит расписанный на четырех листах A4… замечательная чтука должна получиться
Думаю, процесс работы с БД эволюционирует по такой схеме: используем ORM везде, т.к. очень удобно => понимаем, что она тормознутая, и используеп ООП обёртки над SQL => используем ORM только там, где это можно сделать без лишней нагрузки на сервер и на себя.
Скорее эволюционирует по другой схеме — понимаем как работает наша ORM и учимся правильно ей пользоваться.
Вы знаете чем отличается AR от Row Gateway?
AR = Row Gateway + бизнес логика.

Фаулер:
«Иногда шлюз записи данных трудно отличить от активной записи(Active Record). В этом случае следует обратить внимание на наличие какой-либо логики домена; если она есть, значит, это активная запись. Реализация шлюза записи данных должна включать в себя только логику доступа к базе данных и никакой логики домена.»

Кастомизацией роу в зенде можно добиться любого результата.

По поводу выборок зависимых объектов, мы поступили следующим образом (кстати, в трекере зенда есть тикет на эту фичу).
Делается выборка, например, статей: $articles = $table->findLastArticles();
Случайно отправил (

Так вот, потом во вьюхе делается следующее.
foreach…
<?= $article->findParentRow('User')->name ?> //Тут планируется шоткат.

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

Это очень сильно упрощает код и его становится гораздо меньше. Плюс не нужно писать кучу лишних методов и тестировать их, т.к. этот функционал уже протестирован.
Для этого переопределено много логики, в том числе каждая row хранит в себе rowset.

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

А переписать пришлось findParentRow и еще некоторые связанные с ним методы. Все сводится к простому наследованию от нужных классов My_Table extends Ext_Db_Table_Abstract, My_Row extends Ext_Db_Table_Row_Abstract
Спасибо, но я в курсе чем отличается AR от Row Gateway, книжка Фаулера так же лежит рядом, и почитываю ее иногда ;)
Запросы в цикле — это зло, т.к. использование ORM или ActiveRecord никак не противоречит применению join'ов.
Я скажу больше — они иногда и только за применение всяких джойнов и прочих SQL прелестей в нужных ситуациях.

Хотя я вот никак не могу сейчас соединить воедино запросы в цикле и join. Хотя может быть я не настолько знаю SQL, что бы с помощью джойна вытащить одним запросом связанные сущности для нескольких других связанных сущностей…
Тут несколько раз проскакивала тема про выборку зависимых объектов одним запросом, с использованием джойнов. Отвечу сразу всем).

Предположим что мы используем ZF table, кастомизируем его row и определяем там бизнес логику. Когда мы в какой то row делаем выборку этой сущности и джойном тянем другую сущность, то мы фактически в один класс row загоняем данные которые ей не принадлежат. В zf не реализовано расфасовывание объектов по своим row, отсюда возникает куча проблем.
Например мы достали статьи и к ним приосединили юзверей. При выводе статей нам нужно выводить создателей этих статей и рядом показывать значек статуса пользователя (онлайн/офлайн).
В row_user у нас был метод isOnline() который содержал какую то простую логику. А в row_article этого метода нет. Что нам остается делать? Либо писать эту проверку прямо в шаблоне (зло), либо дублиовать этот метод в row_article (зло), либо реализовывать разделение объектов (не очень просто) и самый простой и доступный способ это тянуть данные без использования джойнов через findParentRow().

Можно конечно возразить что findParentRow(), тянет данные для каждого объекта и это запросы в цикле, но ничто не мешает его переопределить так чтобы подтягивались данные сразу для всего rowset, только при первом обращении к findParentRow.
Еще забыл про использование хелперов, но это тоже не тру вей. Разделение логики и данных уводит нас от ООП и в итоге мы получим кучу функций в виде хелперов и кучу объектов представляющих собой просто контейнеры разнородных данных.
Предположим что мы используем ZF table, кастомизируем его row и определяем там бизнес логику.

Паттерн Row Data Gateway выполняет роль удобного взаимодействия с БД, бизнес-логика, как правило, реализуется в другом слое, который расположен над этим.
В данном случае нет правила. Делают как удобно. Я выше писал про то что разбавляя Row Data Gateway бизнес логикой мы получаем AR. И это проще чем писать еще один класс на каждую таблицу к уже существующим двум классам Table и Row. При этом если Вам хочется вынести логику выше, потому что Вы считаете так правильнее, то почему бы и нет. Ведь тогда мы придем к преобразователю данных, разделив взаимодействие c базой и логику на две паралелльные иерархии классов, но за это придется платить сложностью.
>Ведь тогда мы придем к преобразователю данных, разделив взаимодействие c базой и логику на две паралелльные иерархии классов, но за это придется платить сложностью.
Как-то мы с вами уже спорили на подобную тему… и если уж выносить логику выше в отдельную иерархию классов, то надо отказываться от Row совсем. Иначе получится путаница — 2 места для добавления новой функциональности. Применять сразу оба подхода в рамках одного компонента нецелесообразно.
На самом деле все еще сложнее), просто мы обсуждали не это и я специально не заострял внимание на деталях.
Не вижу сложностей =). Либо вы кладёте логику в Row, либо в метод модели. На вкус и цвет, как известно, никого нет.
Sign up to leave a comment.

Articles