Шаги к MVC или как правильно (или неправильно) сделать проект

агрегировать/наследовать
Поясни термины.
Агрегация - включение. Я показал что можно подгрузить любой шаблон в другой шаблон. Можно еще cделать так:
PHP:
...
  $t->build('root->CONTENT','other_content.tpl.html');
...
[php]

Те в скрипте поменять файл шаблона.

А что значит наследовать?

[size=1][i]-~{}~ 19.11.04  13:36:[/i][/size]

[b]Screjet[/b]
[quote]Ненужно заменять одним вариантом, это не дает никаких преимуществ[/QUOTE]
У меня реализованно и так и этак :)
 
Код:
+-- Родитель ----------------+
| +-- Главный -------------+ |
| | +-- Включение 1 ------+| |
| | | +-- Включение 1 --+ || |
| | | |                 | || |
| | | +-----------------+ || |
| | +---------------------+| |
| | +-- Включение 2 ----+  | |
| | |                   |  | |
| | +-------------------+  | |
| +------------------------+ |
+----------------------------+
Вызов (обращение) идет а Главному.
Главный агрегирует включение 1. Включение 1 агрегирует собственное включение 1.

А вообще лучше чтото про ООП почитать, типа Гради Буч.
 
Это был ответ на вопрос:
А что значит наследовать?
?

Что такое наследование в ООП я знаю. Я не понял как шаблон может наследовать другой шаблон. И главное в чем смысл такого наследования. А то что ты нарисовал я тебе уже показал выше. Одни шаблоны включают другие. другие включают третьи...
 
Возможность назначать шаблон верхнего уровня и входить к нему в подчинение а родитель, в свою очередь проверяет дочерные методы/свойства отображения(вывода) и заменяет собственные на дочерные (виртуальный вывод).
 
Так, что то я тоже начинаю терять нить смысла...
Можно пояснить на примере реальной задачи и желательно с кодом?
 
Так, что то я тоже начинаю терять нить смысла...
Верно подмечено тобой в паралельной теме, что необходимость и смысл приходит с опытом.
Можно пояснить на примере реальной задачи и желательно с кодом?
Есть сайта, в нем основная область рекламирует продукт. Сий объект (рекламы продукта) влияет на базовый шаблон и показывает потребителю вместо новых поступлений (описанных в родительском/базовом шаблоне) список товаров из той же ценовой категории.
 
Не заметил данной темы и создал подобную в соседней ветке.

Концепции современной CMS

Модераторы, за что им спасибо, перенесли ее из оффтопика в данный раздел.

Вкратце расскажу о своей идеи (более подробно можно почитать в упомянутой выше теме).

Имеется некий движок, содержащий DBAL, классы для упрощения работы с БД, Create / Delete / Update класс ect. Добавление, удаление, изменение данных по стандартному принципу. А вот для отображения информации следующая технология.

1. Каждый модуль имеет отдельный класс, содержащий методы отображения. Если использовать маски (об этом ниже), зачастую хватает 3-4 методов на целый класс.
2. Шаблоны не привязаны ни к одному модулю. Принадлежность может использоваться лишь для того, чтоб облегчить их поиск и редактирование.
3. Для каждого действия (action) по отображению (!) необходимо зарегистрировать обработчик, а так же указать правила для передаваемых параметров. Действие НЕ связано ни с одним из модулей. В общем случае в обработчике производится дополнительная проверка параметров либо какие-то другие нетривиальные действия. В частном случае – просто указывается карта (начальный шаблон) для парсинга.
4. В карте можно использовать методы отображения любого модуля. Методы могут возвращать шаблоны, содержащие вызовы других методов (ограничений по вложенности нет).

Преимущества такого способа:
1. Шаблоны не зависят от конкретного модуля.
2. При помощи разных шаблонов и одного и того же метода отображения можно строить различный контент, без необходимости правки кода.
3. В соответствие действиям (action) ставятся не с модули, а с шаблоны.
4. Нужные классы подключаются динамически. Всегда вызывается сначала конструктор, затем метод сам отображения. Объекты можно кэшировать для увеличения производительности.




Простой пример с лентой новостей (для просты без разделения на категории).
Создаем методы отображения:
News::List - список новостей.
News::Full - данные об одной новости.

Регистрируем экшены (для простоты все параметры упущу):
main – главная страница, содержащая аннотации к последним 5 новостям, и заголовки с 5 по 10.
full – полная новость по заданному ID и список новостей по теме.
archive – заголовки всех новостей.

И создаем нужные шаблоны:
Для main:
{# News::List("main_news_list", undef, { Limit => [0, 5]) } #}
{# News::List("main_news_title_list"), undef, { Limit => [5, 10] #}

Для full:
{# News::Full("full_news_all", { news_id => {# param.id #} } #}
{# News::List("full_news_title_list"), { theme_id => { # news.theme_id# } } #}

Для archive:
{# News::List("archive_news_title_list") #}

Шаблоны main_news_list, main_news_title_list и т.д. не описываю. Это тройные шаблоны (верх, повторяющаяся строка, низ) которые могут содержать вызовы методов отображения. full_news_all – обычной одинарный шаблон.

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

Более сложный пример с кодом приведен здесь:
http://feotast.net/wcp.html
 
Screjet
Есть сайта, в нем основная область рекламирует продукт. Сий объект (рекламы продукта) влияет на базовый шаблон и показывает потребителю вместо новых поступлений (описанных в родительском/базовом шаблоне) список товаров из той же ценовой категории.
то есть у тебя Представление указывает, какие данные надо показывать на сайте ?
 
Оффтопик.
2 Макс. Тебе Фаулера через сколько прислали? Есть ли смысл искать в книжных магазинах Украины, в частности в столице?
 
2NetFly
хмм, наверное я там некорректно выразился.
У нас просто в городе в книжном магазине можно заказывать книги, а они потом уже сами раз в месяц едут в издательство или посредникам (точно не знаю) и закупают заказаные книги.
А ссылку я дал, чтобы показать, о какой книге я говорил
 
то есть у тебя Представление указывает, какие данные надо показывать на сайте ?
Сложно сказать.. Я не могу четко сказать где у меня модель, где контролер, а где представление.
Есть загрузчик (наверно контролер) который загружает шаблотрон (представление), если представление обнаруживает включения/родителя, то в своем пространсве запускает для каждого загрузчик. Паралельно с представлением загружаются модели, которые и выполняют полезную работу. И т.д. Все построено на рекурсии (кроме независимых моделей).
 
Хочу поделиться мнением по поводу обсуждаемой выше проблемы обработки ошибок.

Предположим, есть некий класс Модели со стандартным набором методов add / edit / delete.

Ошибки можно условно разделить на два типа:
1. Неправильные параметры, переданные в метод.
2. Остальные ошибки, такие как невозможность доступа к файлу из метода и т.д.

Проверкой параметров, полученных от пользователя (get / post / cookies) должен заниматься Контроллер. Раньше мне для этих нужд хватало метода, который проверял минимальную / максимальную длинны, соответствие регулярному выражения, умел работать с callback функцией для дополнительной проверки, а так же устанавливать значение по умолчанию. Сейчас я перехожу на схему правил и фильтров (очень хорошо эта схема реализована в HTML_QuickForm). Все классы Модели могут быть уверены в том, что они получают проверенные данные, которым можно довертеть.

Благодаря такому подходу можно производить обработку ошибок первого типа вне классов Модели. Ниже я опишу используемый мной метод:
1. Регистрируем обработчик для действия (action):
PHP:
$self->SetCmdHandler(‘add', 'OnAddDo', 
{ 
name => $self->charg(50, 1, undef, '^[a-zA-Z0-9_]+$'), 
value => $self->charg(0, 1)
});
Методу SetCmdHandler в качестве параметров мы передаем имя действия, метод, который должен его обрабатывать, хеш, ключи которого являются именами параметров, а значения – правилами. Для правила указывается максимальная длинна, минимальная длинна, undef, регулярное выражение.
2. Описываем метод:
PHP:
sub OnAdd
{
    my ($self, $cargs, $failed) = @_;
    if (!$failed) {
        my $args = $self->CargToArg($cargs);
        my $obj = Object->new();
        my = $obg->add($args); 
        if (!$code) {
            # все нормально
        } else {
            # ошибка, обрабатываем
        }
    } else {
         # ошибка в параметрах, обрабатываем
    }
}
Как все работает. Пользователь переходит по ссылке с некоторыми параметрами (в нашем случае – сабмитит форму). Контроллер ищет необходимый action (если не находит – вызывает специальный обработчик по умолчанию), находит, выполняет проверку описанных параметров. Затем он вызывает соответствующий обработчик и предает ему два параметра: $cargs – хеш, ключи которого имена параметров, а значения – ссылка на массив, содержащий значение параметра, а так же код ошибки (0 – если нет ошибки, 1 – неверная минимальная длинна, 2 – неверная максимальная длинна и т.д.). $failed – булево значение, истинно тогда, когда хотя бы один из параметров не прошел проверку. Таким образом, в обработчике мы можем как среагировать и обработать нужным нам образом ошибки в параметрах, так и на ошибки, которые произошли в методе класса Модели (путем обработки кода возврата). Я, например, для форм просто вывожу список ошибок перед самой формой, а для обычных get запросов – стандартный текст по типу "Вы перешли по неверной ссылке".

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

Может у кого есть варианты получше? Делитесь опытом =)
 
2NetFly
Проверкой параметров, полученных от пользователя (get / post / cookies) должен заниматься Контроллер
То есть например при регистрации юзера, правильность ввода e-mail или уникальность логина проверяет контроллер ?
ИМХО это обязаность модели User.

То как у меня была организованан модель в старом фреймворке я описал здесь
Но с выходом ПХП5 я некоторые вещи изменяю, сейчас фреймворк на стадии обдумывания/написания. Ошибки обрабатывать я планирую примерно так (только пока не решил, использовать классы или функции)
 
То есть например при регистрации юзера, правильность ввода e-mail или уникальность логина проверяет контроллер ?
ИМХО это обязаность модели User.
Правильность e-mail, валидность кредитной карточки, правильность даты и т.д. проверяет контроллер. Какой смысл реализовывать все это в классах Модели, если это не специфические проверки, а общие? Контролер знает, какие параметры нужно передать методу класса Модели, а метод, в свою очередь, знает, что получит от Контроллера валидные данные. Удобная и прозрачная схема.

А вот уникальность логина, e-mail и т.д. должны проверятся соответствующими методом класса User. Причем это можно сделать несколькими способами:
1. Выполнять проверки непосредственно в User::Register и возвращать соответствующие коды ошибок в обработчик, где разбираться с ошибками удобным образом:
PHP:
my $user = User->new(); 
$user->SetAll($args); 
my $code = $user->Register();
if (!$code) { 
    все нормально 
    return 1;
} else { 
    ошибка, обрабатываем 
    return 0;
}

2. Стандартным способом.
PHP:
my $user = User->new();
unless ($user->SetEmail($args->{email})) {
    обрадатываем
    return 0;
}
unless ($user->SetLogin($args->{login})) {
    обрадатываем
    return 0;
}
$user->Register();
return 1;

Хотя первый способ менее правильный с точки зрения ООП, я пользуюсь именно им, так у меня имена параметров совпадают с именами аргументов, которые требует методы, и последовательный вызов Set* в моем случае бессмыслен. Кроме того, в первом случае удобней реализовывать обработку ошибок тем же кейзом.
 
2NetFly
Контролер знает, какие параметры нужно передать методу класса Модели,
откуда контроллер берет эту информацию?

По проверкам. Получается что у тебя данные проверяются в двух местах: в контроллере и в модели.
Возьмем ту же регистрацию. Допустим есть условие, e-mail должен быть уникальным. Как тогда выглядит код получения списка всех ошибок заполнения формы.
У меня все проверки в одно месте, поэтому и список ошибок я могу получить в одном месте :
PHP:
$user = new User();
if (!$user->add()) {
    $errors = $user->errors; // получаем список ошибок
}
все элементарно и просто.
 
>> откуда контроллер берет эту информацию?
В общем случае для каждого параметра, который требует метод, должно быть описано правило. Проверенный хеш параметров передается методу.

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

1. Единожды создав фильтр, например, для проверки e-mail я могу использовать его перед передачей параметров в любой метод, который должен получать валидный e-mail. Если я захочу усовершенствовать или переписать фильтр, мне не нужно будет ковыряться во всех классах.
2. Я могу гибко управлять правилами для параметров. Пример, где это может пригодиться: на форуме ограничения на количество символов в сообщение зависит от группы пользователя. Решить можно так:
PHP:
$self->SetCmdHandler('add', 'OnAddDo',
{ message => $self->charg($user->GetVar('message_max_length') }
);
(Первый параметр в charg – максимальная длинна).
3. Я использую эту же схему для отображения данных (когда не нужно ничего заносить в БД). Например, при просмотре списка юзеров, пользователь может указать, по какому полю и в каком порядке должен быть отсортирован спиок. Я проверяю переданные им GET-ом параметры и в случае их инвалидности устанавливаю значение по умолчанию. Т.е. параметры у меня в любой ситуации после проверки имеют валидные значения:
PHP:
$self->SetCmdHandler('list', 'OnList', {
     orderby => $self->charg(0, 0, undef, '^(user_name|user_email|user_login)$', undef, 'user_name'),
     ordertype => $self->charg(0, 0, undef, '^(asc|desc)$', undef, 'asc')
});

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

>> Как тогда выглядит код получения списка всех ошибок заполнения формы.
Опишу через несколько часов. Вкратце: есть некоторый механизм для обработки стандартных ошибок:
После должно быть заполнено.
Поле содержит х символов. Максимальная допустимая длинна – y сиволов.
Поле содержит х символов. Минимальная допустимая длинна – y сиволов.
Поле содержит недопустимые символы.
И т.д.
 
1. это можно реализовать и в методе модели (выше я давал ссылку в ЖЖ на то, какой я вижу проверку данных внутри этого метода). Там тоже достаточно один раз переписать класс/функцию проверки e-mail для всех моделей

2. Может быть здесь и есть смысл. Но я бы использовал здесь реестр объектов. То есть просерку делал бы внутри модели, а объект текущего юзера я бы брал из реестра объектов.

3. Все это (проверки, установка значений по умолчанию) можно сделать и в моделе. В чем преимущества именно того, что проверки в контроллере делаются ?
 
Я начну с ответа на третий пункт. Можно в базовом классе Модели реализовать механизм проверки ошибок и использовать его во всех конечных классах Модели (News User и т.д.). В этом случае, вполне возможно (я не пробовал, поэтому утверждать не могу) можно будет сделать все те вещи, которые я использую. В таком случае, для методов CDE (Create / Delete / Edit) никакой принципиальной разницы между двумя подходами не будет.

Однако, у меня специфический подход для отображения информации и реализации Представления (гибрид callback и pipeline). Подробно я написал об этом несколькими постами выше (http://phpclub.ru/talk/showthread.php?s=&postid=405398#post405099). Кратко: у меня существуют методы отображения, которые могут использоваться для построения совершенно различных блоков и принимают в качестве параметров шаблон, который используется для построения, и дополнительные параметры. Для каждого действие (action) определен обработчик (точно так же, как для CDE действий) и начальный шаблон. Но шаблон не связан ни с одним классом Модели и в нем могут использоваться любые методы отображения. Механизм проверки в методах отображения реализовать нельзя, поэтому единственным решением является возложение этих задач на Контроллер. Для совместимости, обработку ошибок в CDE я реализовал таким же образом.

Как я уже говорил выше, для CDE методов никаких преимуществ такого подхода может и нет. Однако, нет и ощутимых недостатков (по крайней мерея я их не вижу).

-~{}~ 20.11.04 20:30:

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

-~{}~ 20.11.04 20:45:

Распечатал и внимательно прочитал пост на webscript. Использую точно такой же подход, как и ты для реализации базового класса Create / Delete / Edit. Вот только название у меня менее удачное %) Класс содержит конструктор, методы Select (он же используется для проверки существования записи с ID) / Create / Edit / Delete, а так же соответствующие виртуальные методы (vm Select, ..., vmDelete). В конструктор передаю имя таблицы, имя primary key, булево значение, указывающее на то, является ли primary key полем с типом автоинкремент. Не придумал ли ты каких-нибудь дополнительных приятных фич? Ничего не приходило в голову?

-~{}~ 21.11.04 03:01:

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

-~{}~ 21.11.04 21:18:

Тема для обсуждения валидаторов: http://phpclub.ru/talk/showthread.php?s=&threadid=5886 .

-~{}~ 21.11.04 21:20:

Ошибся ссылкой, а отредактировать сообщение уже нельзя. Вот верная ссылка: http://phpclub.ru/talk/showthread.php?s=&threadid=58865 .
 
Я вот CMS пишу по старинке, в одном скрипте контроллер и представление.
У меня контроллер и представление - каждый в своем классе, соответственно в своем файле.
 
Сверху