Как стать автором
Обновить

Комментарии 81

Случайно запостил дважды.
Сейчас занимаюсь написанием ORM-системы на базе Zend Framework. Цепочки вызовов(аки fluent interface) есть. Например, сейчас можно делать так:

$data = Orm::factory('table_name') -> loadAll(7) -> getArray();
$data['column_name'] = 'new_value';
Orm::factory('table_name') -> loadAll(7) -> populate($data) -> save();

Соответствено загружает данные, меняет содержимое одного столбца в у 7го элемента и сохраняет на место.
Пока все идет ничего, но тормозит страшно. Может у автора есть советы по правильному отражению объектов реляционной базы?

P.S. да запросы кэшируются, поэтому в данном примере идет всего 2 (а не 3) запроса к БД.
я только что добавил абзац про отложенный рендеринг.

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

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

В данном случае как раз это и происходит… Загрузка данных в память, изменение в памяти, апдейт в базу. ORM универсален, а за универсальность, как правило, надо платить скоростью.
Не туда ответил =)
Особенность многих ORM — работа с WHERE-выражениями.

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

А эта функция будет работать с подмножеством данных, добавив своё дополнительное выражение WHERE.

При этом де факто выборка данных произойдёт только в самый последний момент, сразу по комбинированному условию WHERE.
Zend Framework позволяет работать с ORM и без этих велосипедов. Используйте наследование от Zend_Db_Table и Zend_Db_Table_Row.
Я так и делаю. Orm::factory() выдает объект, который наследуется от Zend_Db_Table так что доступны и его функции. :-)
Ой как мне не нравится, честно говоря, зендовский ORM после ActiveRecord-то. Я понимаю, что там подход отличается от ActiveRecord, но вот где бы найти под php что-нибудь не монструозное из ORM, пусть даже не полнофункциональное?
Хорошие идеи, возьмём на вооружение =)
У меня принцип такой.

— Ядро передаёт ссылку загрузчику объектов, тот грузит объект.
— Ядро вызывает метод content() и выводит результат

Загрузчик объектов:
— Находит нужный класс по его имени или по привязке форматов URL к классам.
— Создаёт объект класса.
— Инициализирует класс.

Инициализаторы классов могут быть разные. Самый популярный — это, конечно, использующий mysql-бэкенд, выполняющий ORM-маппинг. Хотя может быть и иным (часто практикую загрузку их статических *.txt, реже — XML или Oracle) или вообще отсутствовать (тоже весьма распространено).

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

Скажем, нужно вывести список товаров на страницу.

Объект этой страницы автоматически, при создании, ничего из БД не грузит.

Но метод content() вызовет метод local_template_data_set(), возвращающий хеш переменных для насыщения шаблона.

Вот этот метод, как раз, и может описать загрузку объектов. Например, так:

function local_template_data_set()
{
    return array(
        'list' => objects_array('my_cool_tovar', array(
            'category_id' => $this->category_id(),
            'is_published' => 1,
            'page' => $this->page(),
            'per_page' => $this->items_per_page(),
            'order' => 'title',
        )),
    );
}


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

А вот в случае более сложном, если есть другие объекты, вызываемые выводимыми, и в шаблоне потребуются их параметры, при выводе добавится ещё N(=число_объектов на странице) запросов для загрузки эти подобъектов.

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

Получив список нужных к выводу объектов в local_template_data_set() не сразу возвращаю результат, а извлекаю нужные id и получаю массив ID для загрузки вторичных объектов. И загружаю их одним запросом. Результат не использую, но объекты при этом кешируются. И потом, при выводе страницы, они будут браться уже из кеша.

Скажем, нужно вывести страницу топика форума и для каждого постинга — получить объект пользователя. Для простоты не показываю разбивку на страницы.
$posts = objects_array('forum_post', array('topic_id' => $this->id()));

$user_ids = array();

foreach($posts as $p)
    $user_ids[$p->user_id()] = true;

objects_array('forum_user', array('id' => array_keys($user_ids)));

return array('posts' => $posts);


В итоге в шаблоне можно сразу использовать:
{foreach from=$posts as $x}
<p>{$x->create_time()|smart_time} {$x->user()->title()} пишет: {$x->body()}.</p>
{/foreach}


$x->user() в class forum_post вызовет object_load('forum_user', $this->user_id()), но нового обращения к БД уже не будет, так как все объекты уже загружены и инициализированы.

Так в этом примере в итоге получается лишь два простых запроса к БД.
У вас используется пообьектная модель.

предположим, что в процессе рендеринга стало ясно, что надо вывесит 200 комментариев от 50 пользователей.

тогда в случае режима с поддержкой отложенного рендеринга мы получим только два запроса — выборка набора комментариев, и выборка 50 профилей.

У Вашем же случае — 250 запросов, если не ошибаюсь.
Хотя ошибаюсь, ен очень внимательно прочитал.

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

Но всё равно подход получается не стройный.
>У Вас используется некая структура сбоку, вместо модели документа, чтобы получить схожий результат.

Да, у меня используется лишнее действие. Перед отдачей данных шаблону, происходит ручная предвыборка вторичных объектов.

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

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

А что конкретно не нравится?

Понятие отдельного метода класса, который занимается данными для шаблона?

Это обычно удобнее, чем выдёргивать нужные данные из шаблона. Считаю, что работу с извлечением данных в шаблоне должна минимизироваться :)

Есть идея лучше? Давайте обсудим, тем более, что фреймворк у меня очень легко перенимает почти любые альтернативные парадигмы :)
Хочется реализовать схему:

1) Обьектная модель документа (блоки HTML, профилей пользователя, меню и прочая).

2) Выходной фильтр, преобразующий модель документа, например, к HTML.

Суть в том, что большая часть данных нам совершенно не нужна вплоть до этапа этапа формирования HTML.

И именно на этом этапе можно, например, получить по PageDOM набор внутренних ссылок на документы, и одним махом преобразовать из в красивые ссылки. Или получить по PageDOM список блоков «профиль пользователя», одним запросом получить все данные, и вызвать блоку render по исходному множеству.

Главная беда большинства современных CMS — они мыслят на уровне HTML.
А метод использования PageDOM позволит раобтать именно со множествами, и в большинстве случаев не ранее, чем потребуется данные отрендерить.

Что резко ускоряет работу с наборами записей (новостные ленты, комментарии и прочие группы обьектов).
Фишка в том, что такая структура сильно повысит нагрузку на код. И в результате можно очень капитально проиграть в итоговом быстродействии более классическим структурам.

Что в этом примере, что у меня — запросов будет только два. На запросах выигрыша нет. Паритет.

По сложности восприятия на уровне низкого вхождения у меня явно проще, на высоком уровне при грамотной реализации вариант PageDOM возможно, будет проще. Хотя даже это не факт. Но пусть будет паритет :)

По скорости PageDOM потребует намного больших вычислений, что в случае PHP весьма накладно. Тут PageDOM в минусе.

Итого, получается чистый минус :)
Вообще, если брать простые случаи, то концепция PageDOM — это всего лишь более общий вариант вашего local_template_data_set.

Но, во-первых, PageDOM более легко описать и предоставить стороннему разработчику для использования и расширения.

Во-вторых, тяжёлой нагрузки на код не будет, по причине того, что эта концепция лаконична.

Вместо HTML мы изначально храним некий многоуровневой хэш, или обьект. Далее выполняем fetch (local_template_data_set?), далее рендерим.

Всего лишь переход от кучи списков в вашем случае к одному дереву.
>Вообще, если брать простые случаи, то концепция PageDOM — это всего лишь более общий вариант вашего local_template_data_set.

В простых случаях (скажем, нужно вывести постраничный список объектов) этот метод у меня может отсутствовать в конечном классе, полностью реализованный в суперклассе :)

>Во-вторых, тяжёлой нагрузки на код не будет, по причине того, что эта концепция лаконична.

Зато нелаконично — парсить шаблон на тему зависимых объектов, которые нужно грузить скопом :)

>Вместо HTML мы изначально храним некий многоуровневой хэш, или обьект. Далее выполняем fetch (local_template_data_set?), далее рендерим.

Так чем тогда это от моего способа отличается? Подключаем к моей системе xml-render-engine (надобности не было, так что не разбирался. Всё, что нужно — это подобрать хороший готовый механизм засовывания хеша в XML) и имеем готовый результат :)
О каком парсинге может идти речь, когда это структура данных, а не обьект?

Любого рода оптимизации наверняка можно выполнить на уровне документа.

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

Обе операции легко оптимизировать (например, формировать списки по классам и пути обхода при добавлении элементов в дерево).

Кроме того, это добавляет новый уровень абстракции и возможность соответствующего кэширования, например, если обьекты документы будут предоставлять информацию для менеджеров кэширования, к примеру.
Ещё один плюс дерева — удобство его модификации сторонними плагинами.
Это плюс по сравнению с чем? :)

Чем «сторонние плагины дерева» лучше функций PHP, модификаторов Smarty или перегружаемого метода класса?
Кхм, Насколько кроректно и поддерживаемо в будущем написать плагин, который переколбашивает XML или даже HTML?

Или расширяет структуры дванных своими данными?

В вашем случае — костыли и непереносимость.
Давай тогда лучше к конкретике. А то, боюсь, мы уже просто разные вещи обсуждаем. Какой плагин можно прикрутить к описываемой системе, но он будет костылём в простой системе хеш — шаблон?
Дерево документа — читайте как хэш.

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

Заполнение хеша, да ещё объектами/хешами, выбираемыми по условиям — это задача нетривиальная в описательном плане.

Вот мне и интересно, как ты это конкретно себе представляешь. Дай небольшой кусок абстрактного кода.

Пока речь о линейной записи — всё хорошо. А дерево? Ну, вот в примере с форумом.
$tree = array(
    'data' => array('posts' => objects_arrays_fields('post', <условие>, 'список полей'...))...
);


А вот как быть в этом случае с загрузкой объектов users, привязанным к posts?

Именно — описание дерева.

Покажи пример на практике.
как я уже говорил, блок «вывод профиля пользователя» — это скорее всего в CMS будет отдельный класс узла дерева (умеющий фетчить узлы своего типа).

В случае с фабрикой конструирование дерева будет, например, таким:

my $posts = Post->select($condition);

предположим, что узлы типа Post на этапе fetch инстанцируют дочерние узлы типа UserProfile и Text.

Узлы типа UserProfile на этапе fetch получают данные по профилям.

на этапе рендеринга выходной фильтр HTML использует соответвующий рендере для узлов Post, которые скорее всего делегируют рендеринг профиля подузлу UserProfile, а вовод узлов Text — рендереру markup.
Пардон, но у нас задача стоит не выводить профиль пользователя, а вывести его имя в заголовке сообщения. И, например, статус этого пользователя на форуме.

Пример фабрики непонятет. Где там указание на извлечение нужных полей данных пользователя? Скажем, нам нужны поля имени пользователя и его статуса на форуме, но не нужны его репутация и штрафы от администрации, которые, в случае если извлекать всё, будут генерировать новые запросы в соответствующие таблицы БД.

Если подходить к вопросу как «выдернем список постов. Посты зависят от юзеров. Выдернем всех связанных юзеров.», то неизбежно продолжение: «юзеры зависят от штрафов от модераторов, штрафы от модераторов cодержат ссылки на модераторов как пльзователей, у пользователей-модераторов есть последние сообщения, последние сообщения принадлежат топику, топик состоит из других сообщений........» :) И мы скоро выдернем всю БД.

Значит надо как-то описывать, что нам нужно в этом конкретном случае, а что — нет. В запросе my $posts = Post->select($condition); такого указания нет.

Давай дальше развивать твой пример :)
отфетчим список постов, нужных нам.

что именно будет отфетчено — детали реализации Post.
Угу. И Post ответчит всех юзеров. Юзеры отфетчат свои последние посты. Посты отфетчат топики. Топики отфетчат новые постинги. И так — много раз подряд.



Нет, нам явно нужно указать что нам нужно. Всю БД фетчить мы не можем.

Или ты считаешь, что User должен различать, откуда дёргают дёргающий его Post? :)
Post по condition сможет либо сконструировать запрос к профилям, лмбо, что скроее всего, сконструирует User->select(join=>post), либо получит набор ids.

Зависит от удобства ORM.

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

В общем, я пока вижу, что ты просто не сталкивался с этой проблемой на практике. Столкнёшься — продолжим обсуждение :) Хотя, сам же ты говоришь, что вопрос это пока для тебя сугубо теоретический, а не практический. Вот когда дойдёт до практики — тогда и увидишь, в чём тут проблема :)
>О каком парсинге может идти речь, когда это структура данных, а не обьект?

В шаблоне вывода топика есть цикл вывода сообщения.

В субшаблоне сообщения есть показ автора сообщения.

В субшаблоне автора выполняется показ его имени.

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

Если автогенерации нет и всё указывается программистом в контроллере вручную, то непонятно, в чём вообще проблема и чем это отличается от моего метода, кроме того, что передавать надо не хеш, а DOM-дерево? :)
* Явным выделением этапа формирования дерева и этапа fetch для объектов дерева.
* Универсальностью и расширяемостью подхода.

Это не отличие от вашего подхода, это его правильная реализация.
И тут тоже давай попробуем перейти к конкретике.

Как ты на уровне кода видишь описание формирования дерева в модели (меня тут правильно поправили, я тормозил и модель обзывал контроллером ;) )? Грубо, чисто как концепт?
Дерево формируется контроллером.

Модель лишь предоставляет данные.

Контроллер же берет на себя шаг fetch, фромируя по построенному дерему необходимые одполнительные данные (вернее, это уже скорее всего делают классы, чьи элементы представлены в дереве).
>Обьектная модель документа (блоки HTML, профилей пользователя, меню и прочая).

Это реально реализовать только через что-то типа XML. Так что тогда мешает сразу делать XML-шаблоны и парсить их любым штатным XSL-процессором? :) Хоть в HTML, хоть во что-то ещё?



Но, ИМХО, это лишняя сущность.

>Главная беда большинства современных CMS — они мыслят на уровне HTML

За других не скажу, но у меня не важно, какой у объекта template_engine. Можно сделать и самоизвлекающий нужные данные XML. На практике фреймворк «мыслит» в сущностях HTML, PNG/GIF/JPEG, XML+RSS.

Но, с другой стороны, а почему бы и в сущностях HTML сразу не мыслить? Если это 99% рынка, а такое сужение позволяет существенно упростить разработку, повысить скорость, гибкость, а кое-где и чистоту MVC?

Есть реальные плюсы у чисто объектного описания документа, перекрывающие массу минусов?
По-моему, вполне хватило бы составления объектной модели документа контроллером, а потом просто использовать нужное представление — определённый шаблон и шаблонизатор
Одним только документом всё равно не обойтись. Как ни крути, но документ не может/не должен заниматься раскруткой URL до нужных объектов. Значит, потребуется некий отдельный код, делающий это. Так почему в этот код тогда не добавить ещё пару строк для извлечения всех оставшихся данных?

А потом полученный хеш скормим XSL-процессору :)



Хотя, как я выше писал, возможен компромиссный вариант. Базовые параметры (topic_id для темы форума, страницы и прочее) выделяет фреймворк. Далее включается XML-парсер и извлекает из шаблона все нужные ему имена данных, занимается сведением этих данных, загрузкой и скармливанием XSL-процессору.

Будет очень медленно, и с тяжёлым не human-readable синтаксисом.

И только один сомнительный бонус — отсутствие вручную написанного кода загрузки данных в контроллере. Но это итак обычно только несколько строк :)
Раскручивать URL должен контроллер, в любом случае, он должен выделять из URL тип документа, определять соответствующий ему шаблонизатор и после составления объектной модели документа просто скармливать её соответствующему шаблонизатору и шаблону
Безусловно. Но тогда почему бы контроллеру кроме этих данных не готовить и другие? :) Всё равно привязка URL не универсальная в общем случае, а объект-специфичная.
это скорее обработка входных данных, и её можно сделать довольно универсальной

хотя я уже целиком в своей идее, потому, наверно, не объективен =)
>хотя я уже целиком в своей идее, потому, наверно, не объективен =)

Ну да, как и я — в своей реализации ;)
как раз сейчас пишу свой фреймворк для одной задачи, стою на роутере урлов, подумал и пришел к выводу что тут нужен некий общий механизм событий, http запросы по сути — события, на них может быть подписаны несколько контроллеров, такой подход позволит удобно расширять систему добавляя обработки на те или иные запросы, нечто похожее на хуки в друпале только более общий подход
Есть контроллер, формирующий документ в терминах DOM.

Есть вид, который документ из терминов DOM преобразует в целевой формат (HTML, XML etc.).
Это может работать у меня прямо сейчас, практически из коробки :)

Насколько я понял, задача стоит шире — сделать формирование данных контроллером универсальным, на основе шаблона документа, которому предстоит скормить эти данные.

Вот это и будет слишком медленно в PHP. Нам придётся делать сложный древовидный парсинг и ловить зависимости отъектов.

Если я не правильно понял, и подразумевается построение контроллера дерева объектов программистом вручную, то, повторюсь, у меня это можно хоть сейчас запускать :) Но не думаю, что это выгоднее имеющегося механизма с хешем базовых данных и представлением, которое само извлекает потом всё, что ему нужно при рендеринге. Разве что только для того, чтобы припахать XML-представление :)
Это почти ваш подход. Но представление не должно извлекать данные само.

Более качественно иметь этап построения хэша, этап фетчинга хэша (расширения реальными данными), реализуемые контроллером.

И этап рендеринга.

Это я пишу, вдруг вам не нравится термин дерево. Для простоты назовите списком )
>Но представление не должно извлекать данные само.

Так оно и не извлекает ничего. Оно обращается к методу класса.

Грубо говоря, {$post->user()->title()}.

Извлечение своих данных — это головная боль самого класса. И это правильный подход. Все внутренние процессы должны быть инкапусированы. Неважно, берётся этот title() из БД, или формируется как first_name().' '.last_name(). Это вопрос класса.
Вот тут и есть разница.

Я предлагаю выделить получение данных в отдельный шаг, чтобы получать их одновременно для всех сущностей одного класса, к примеру.
Какие от этого плюсы и как это отразится на шаблоне? (пример хоцца =))
пример, построено дерево

doc: document
head => :head (title, ...)
body =>
565: pageLink

на этапе fetch для всех обьектов типа pageLink одним запросом будет получен дружелюбный url.

на этапе рендеринга всё это преобразуется в html.
1. Синтаксис для PHP ненативный. Его парсинг будет весьма ресурсоёмким.

2. Тут не примера формирования самой важной сущности — тела доумента. Пусть будет наш пример с телом топика форума.

3. А при чём тут дружелюбные URL и запросы? :) Это же чистый языковый код.
дерево есть родное представление в терминах среды программирования.

Можно представлять его в виде комплексного обьекта или ссылки на хэш.

Ни в коем случае дерево не является чем-то, что нужно парсить (потоком байтов или строкой).
Проблема в том, что дерево у нас будет менять своё наполнение в зависимости от данных.

У разных сообщений — разные пользователи.

Тебе нужно указывать зависимости.

Эта задача не такая тривиальная, как тебе, возможно, кажется на первый взгляд.

Я поэтому и прошу конкретный пример описания простейшей структуры:

Топик -> множество постингов -> пользователи.

Именно то, как ты будешь описывать эту модель в конкретном проекте.
Я не хочу сейчас заниматься написанием CMS, и тратить время на полную проработку архитектуры.
Ок :)
Получается, мы сначала делаем универсальное представление, этакий «байт-код», а потом преобразуем его в нужный формат?

Но единственный плюс — пакетное преобразование вместо одиночного применения к объектам?
Этакое дерево а ля HTML/XML DOM.

Естественно, более высокого уровня, в частности markup code — это просто тип узла дерева, без объектности на уровне разметки (в CMS она не нужна).

Плюсов несколько:
* пакетная обработка, как вы заметили
* удобство встраивания сторонних модулей (плагинов), котроые могут модифицировать дерево (это удобнее, чем модифицировать разметку).
* Легкость добавления новых форматов вывода.
У меня это итак один шаг :)

Но мне интересен той вариант. Именно — как ты собираешься описывать зависимости.

Видишь ли, мне почему-то кажется, что ты не сможешь не дублировать зависимости в дереве дважды. В описании выборки и в шаблоне-описателе. Либо придётся парсить шаблон. Именно поэтому я так и прошу тебя привести пример конкретного описательского кода, который позволит вывести список сообщений топика форума с указанием имён пользователей :) Это очень простой пример.
Вообще, вопрос устранения дубликатов просто решается фабрикой узлов дерева.

Идея у меня примитивная, я ен понимаю, какие примеры нужны.
А откуда фабрика будет брать информацию о строении дерева? Из шаблона? Тогда потребуется его парсинг, о чём я не раз уже говорил.

Пример нужен, чтобы ты почувствовал разницу между примитивной идеей и её формализацией.

Увы, на словах и в мыслях часто многие вещи поначалу кажутся намного проще, чем оказывается, когда дело доходит до практики :)
ОоО… фабрика знает только об узлах дерева.

Парсинг — забудьте это слово. Это применимо только к тексту/потоку.
Это вполне осуществимо, и это то почти-совершенство, к которому надо стремиться =)

Хотел задать вопрос, но теперь, кажется, сам придумал интересную реализацию (как сделать так, чтобы можно было написать шаблон страницы, и, не исправляя код, выводить туда любые доступные в программе данные в нужном виде?)
>как сделать так, чтобы можно было написать шаблон страницы, и, не исправляя код, выводить туда любые доступные в программе данные в нужном виде?

Например, использовать унифицированную систему именования полей объектов. Тогда {$x->title()} будет работать для всех объектов :)

А если от объекта требуется индивидуальный рендеринг — то шаблончик может храниться при нём, а отдаваться по тем же полям — {$x->body()}. При этом body() может заниматься массой умных вещей, вплоть до вопросов кеширования.
я имел в виду, не исправляя код контроллера и моделей =)
при этом я не использую объекты в шаблонах…

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

по поводу заголовка ещё думаю =)
Пардон, но заголовок, запрашивающий данные — это контроллер. Думаю, не правильно держать его в представлении :)



Собственно, у меня каждый объект обычно состоит из двух компонентов — класса и локального шаблона. По сути класс — это контроллер. Шаблон — представление. Зачем их объединять — непонятно :) Тем более, что их разделение повышает гибкость. Я могу использовать разные представления для одного контроллера, могу — разные котроллеры у одного представления.
Вообще-то класс — это модель ;)
Выдержка из Википедии:

Модель (Model). Модель предоставляет данные (обычно для View), а также реагирует на запросы (обычно от контроллера), изменяя свое состояние.

Представление (View). Отвечает за отображение информации (пользовательский интерфейс).

Поведение (Controller). Интерпретирует данные, введенные пользователем, и информирует модель и представление о необходимости соответствующей реакции.

Так что класс — это модель. Шаблон, конечно же, представление. А контроллер — это код, который обрабатывает входные данные (url, post, session) и предоставляет обмен информацией между моделью (которая должна работать с базой данных) и представлением (начиная с локальных и кончая общим шаблоном)

У меня заголовок играет роль «информатора» контроллера о том, какие модели в данный момент нужны. Т.е., к примеру, мы переходим по урлу /blogs/I_make_cms/53396/. Контроллер получает реврайтом нечто вроде /?q=blogs&args=I_make_cms|53396, он определяет, что используем представление «blogs», запускает /views/blogs/index.php, там на основе аргументов формируется запрос моделей, что-нить типа:
loadModels('User', 'Post', 'CommentsList');
Контроллер загружает модели, инициализирует их, если нужно, а потом загружает нужный шаблонизатор и загружает шаблон представления, в котором мы используем нечто вроде
{$User->name}, {$Post->load($Args[1])->content} и т.д.

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

З.Ы. Тут мб холивар оттого, что шаблоны получаются черезчур сложные для дизайнера. Но я могу ответить — зато без оговорок реализует MVC =)
А без объектов, кстати, не получается обойтись =)
>Вообще-то класс — это модель ;)

Да, пардон, был не прав :)

>Контроллер получает реврайтом нечто вроде

Понятно. Мне же не раз приходилось разворачивать систему там, где нет rewrite, либо там, где введение новых правил rewrite усложнено (например, в конфигах сервера). Поэтому у меня одна общая точка входа в систему и парсингом занимается контроллер (в правильной терминологии ;))

>а значит, любое изменение в иерархии урлов можно производить без изменения кода моделей и контроллера…

Это само собой. У меня привязка URL'ов и классов прописывается явно в соответствующих handler-файлах (хотя есть механизмы и неявной привязки к структуре URL, иногда удобно — но они реализованы на общем принципе. Просто, если не находится явная привязка, поочерёдно вызываются классы, повешенные на URL вида ".*", пока не найдётся отозвавшийся).

>
Ну вот мы и пришли к одной точке зрения =) Но реализации будут в корне разные, судя по мной увиденному коду =)
Ну, мою реализацию (ядро фреймворка + расширения моего сайта) можно на hg.balancer.ru посмотреть.

Принципы построения/подключения модулей — в моём блоге первая запись.

А коммерческие сайты на этом движке — www.aviaport.ru или www.rp1990.ru
Многа букаф в коде… Надо проще =) больше классов, методов, меньше по размеру методы. Периодический рефакторинг делать… Если конечно, система не чисто для себя, а всё-таки для масс =)

Ну, не буду критиковать больше — мне пока вообще нечем похвастаться =)
>Надо проще =) больше классов, методов, меньше по размеру методы

Видимо, ты порылся в контроллере. Типичный класс на конечном проекте — это несколько строк. Иногда буквально 3-4 строки :) Но такое чаще в админке, например, вывести постраничный список объектов для редактирования:

Модель:
<?php
class aviaport_admin_directory_person_main extends base_page_paged
{
    function main_class() { return 'aviaport_directory_person'; }
    function title() { return ec('Персоны'); }
}


Представление:
<div class="all-docs">
<ul>
    <li><a href="new/"><b>Добавить персону</b></a></li>
</ul>
</div>

{$this->pages_links('all-docs')}
<table class="btab w100p">
<tr><th>ID</th>
    <th>Персона</th>
</tr>
{foreach from=$items item="x"}
<tr><td>{$x->id()}</td>
    <td>{$x->titled_admin_url()}</td>
</tr>
{/foreach}
</table>


В том, что отдаётся юзеру, конечно, классы уже побольше. Там надо заниматься настройками, обвязками, конфигурированием… Итого размер актвных рабочих классов колеблется от 500 байт до пары-тройки килобайт.

По числу классов… Ядро:
$ find /home/balancer/work/programming/php/bors-core/ -name '*.php' -exec cat {} \;|grep -P '^class \w+'|wc -l

99

Надо ещё один класс дописать ;)

www.aviaport.ru:

303 класса.

Нормально, куда больше-то? :) Одна сущность — один класс.
Ну, я имел в виду контроллер, конечно же… Слишком много там функций =) мои конфиги в последнее время ужимаются до 10-20 строк объявлений констант… Конечно, контроллер слегка разрастается в плане количества файлов, но зато открываешь один из них — и всё по полочкам ;)
Контроллер имеет практически минимальный для своего функционала размер :) Он слишком много тонкостей учитывает. Как я уже говорил, код далеко не под один проект заточнен (сейчас мною поддерживаемых один некоммерческий, штук пять коммерческих, ещё два в разработке — и почти в каждом случае масса своей специфики) — это накладывает отпечаток.

Вообще, система изначально была микроядерной с рядом разного калибра расширений. Нынешний объектный контроллер был только одним из вариантов. Потом однажды я понял, что за целый год всё новое писал только на нём и, таким образом, его функционал и расширяемость меня полностью удовлетворяет. В итоге ядро было переписано с интеграцией объектного расширения прямо в него. А старый код — частично переписан на объекты, частично пока ещё крутится на старой системе.

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

Ну и ещё масса чёрной работы по связыванию компонентов с классов вынесена в контроллер. Скажем, нужно искать представление рядом с файлом модели. Шаблон рядом с классом. Когда-то в каждом классе была строка function class_file() { return __FILE__; } Несложно, но это лишняя строка. И почти в каждом классе. Сейчас этим озабочен контроллер. Когда-то родителей объекта требовалось обязательно указывать методом parents(). Но в 90% случаев родитель только один и является надкаталогом в URL. Сейчас, если так, этим заведует тоже контроллер. И т.д. и т.п. :)
Достаточно просто все данные, которые мы хотим отдать пользователю, представить в DOM.

Обычное поле (field), либо экземпляр типа DomField, по настроению.
В частном случае — два запроса :)

Один — генерация объектов комментариев по тому или иному критерию.

Потом сбор ID пользователей, обычно — PHP-циклом.

Потом второй запрос — загрузка объектов пользователей. Хотя напрямую я эти объекты использовать не буду, но они закешируются. И потом, при 50 загрузках объектов этих пользователей, они будут браться не из БД, а из кеша.

Я выше этот момент описал :)



Можно извратиться и сделать вообще один запрос, прописав все нужные параметры пользователя прямо в объекте комментария, но это сложно, некрасиво и может привести прямо к потере производительности, т.к. на сложных БД JOIN может быть много тяжелее двух простых вызовов.
У вас используется пообьектная модель.

предположим, что в процессе рендеринга стало ясно, что надо вывесит 200 комментариев от 50 пользователей.

тогда в случае режима с поддержкой отложенного рендеринга мы получим только два запроса — выборка набора комментариев, и выборка 50 профилей.

У Вашем же случае — 250 запросов, если не ошибаюсь.
Модули CMS должны формировать не HTML-код, а обьектную модель документа
Дальше даже читать не стал… Потому что сразу захотелось написать коммент, что я на 100% согласен с этим утверждением! :) Вот тут сумбурно описал свои мысли: torqueo.net/xml-and-build-interface/

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

Публикации

Истории