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

_RVK_

Новичок
агрегировать/наследовать
Поясни термины.
Агрегация - включение. Я показал что можно подгрузить любой шаблон в другой шаблон. Можно еще 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]
У меня реализованно и так и этак :)
 

Screjet

Новичок
Код:
+-- Родитель ----------------+
| +-- Главный -------------+ |
| | +-- Включение 1 ------+| |
| | | +-- Включение 1 --+ || |
| | | |                 | || |
| | | +-----------------+ || |
| | +---------------------+| |
| | +-- Включение 2 ----+  | |
| | |                   |  | |
| | +-------------------+  | |
| +------------------------+ |
+----------------------------+
Вызов (обращение) идет а Главному.
Главный агрегирует включение 1. Включение 1 агрегирует собственное включение 1.

А вообще лучше чтото про ООП почитать, типа Гради Буч.
 

_RVK_

Новичок
Это был ответ на вопрос:
А что значит наследовать?
?

Что такое наследование в ООП я знаю. Я не понял как шаблон может наследовать другой шаблон. И главное в чем смысл такого наследования. А то что ты нарисовал я тебе уже показал выше. Одни шаблоны включают другие. другие включают третьи...
 

Screjet

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

_RVK_

Новичок
Так, что то я тоже начинаю терять нить смысла...
Можно пояснить на примере реальной задачи и желательно с кодом?
 

Screjet

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

Концепции современной 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
 

Макс

Старожил PHPClub
Screjet
Есть сайта, в нем основная область рекламирует продукт. Сий объект (рекламы продукта) влияет на базовый шаблон и показывает потребителю вместо новых поступлений (описанных в родительском/базовом шаблоне) список товаров из той же ценовой категории.
то есть у тебя Представление указывает, какие данные надо показывать на сайте ?
 
Оффтопик.
2 Макс. Тебе Фаулера через сколько прислали? Есть ли смысл искать в книжных магазинах Украины, в частности в столице?
 

Макс

Старожил PHPClub
2NetFly
хмм, наверное я там некорректно выразился.
У нас просто в городе в книжном магазине можно заказывать книги, а они потом уже сами раз в месяц едут в издательство или посредникам (точно не знаю) и закупают заказаные книги.
А ссылку я дал, чтобы показать, о какой книге я говорил
 

Screjet

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

Предположим, есть некий класс Модели со стандартным набором методов 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 запросов – стандартный текст по типу "Вы перешли по неверной ссылке".

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

Может у кого есть варианты получше? Делитесь опытом =)
 

Макс

Старожил PHPClub
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* в моем случае бессмыслен. Кроме того, в первом случае удобней реализовывать обработку ошибок тем же кейзом.
 

Макс

Старожил PHPClub
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 сиволов.
Поле содержит недопустимые символы.
И т.д.
 

Макс

Старожил PHPClub
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 .
 

Alexandre

PHPПенсионер
Я вот CMS пишу по старинке, в одном скрипте контроллер и представление.
У меня контроллер и представление - каждый в своем классе, соответственно в своем файле.
 
Сверху