Правильное разделение ответственности

skwee

Новичок
Хотелось бы узнать кто за что отвечает.
Рассмотрим данный пример (не сферический вакууме):
Регистрация пользователя.
Ест Контроллер, как то так:
PHP:
class RegisterController extends AbstractController {
    public function run() {
        if($this->getRequest()->isPost()) {
            if(MagicValid($this->getRequest()) {
                MagicUserCreate($this->getReqest());
            } else {
                $this->getView()->errors = MagicErrors();
            }
        }

        $this->getView()->a = $a;
        return $this->getView();
    }
}
тут есть 2 магических функции:
1. MagicValid + MagicErrors - как понятно из имени это валидация формы. Кто должен этим заниматься? Если форму валидирует контроллер, то при простой форме регистрации (username + email + password + agree to terms) контроллер может раздуться. И с другой стороны, если пользователь хочет поменять юзернейм потом, то в контроллере ChangeUsernameController будет та же валидация что и в регистер. Логично ли создать некий слой валидации? Как глубоко он должен валидировать формы (duplicate email в бд входить в его ответственность или нет, если нет то в чью да? модель?)?

2. MagicUserCreate - Собственно создания пользователя. Это не тупо insert into а в данном случае надо создать юзера в бд, создать Activation code, приготовить и отправить мыло с данным кодом, авторезировать юзера. Не хочется засорять модель юзера и делать что то вроде:
PHP:
class UserModel extends Model{
    public function create($data){
        $userid = $this->insert($data);
        $activationCodesTable = new ActivationCodesModel();
        $code = $activationCodesTable->create($userid);
        $email = new Email('activationCode.phtml', array('code'=>$code), $data['email']);
        $email->send();
        CurrentUser::instance()->authenticate($data['email'], $data['password']);
    }
}
(там есть проверка ошибок)
Как быть в данном случае? Кто должен отвечать за создания\модификацию\удаления ресурсов? Есть ли смысл вводить слой Action-ов или Сервисов? А что тогда делать с простыми акшенамы вроде user edit где все что надо сделать это проверить дату (смотри пунки 1 - валидация) и сделать update в базе.

Спасибо за ранее!
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
это валидация формы. Кто должен этим заниматься?
модель, только она знает структуру данных
Логично ли создать некий слой валидации?
нет, только в качестве пре-валидации на js перед сабмитом формы,
впрочем, есть такая хорошая штука, как QuickForms, которая хорошо делает формы, и я еще не решил что с ним делать

Как быть в данном случае?
ActiveRecord :)
Кто должен отвечать за создания\модификацию\удаления ресурсов?
Только модель.
Есть ли смысл вводить слой Action-ов или Сервисов?
конечно
с простыми акшенамы вроде user edit
это не простой экшн, простой - это pageView, когда все, что надо - вывести страницу,
хорошие экшены - строк на 5-10, а класс контроллера может объединять родственные экшены
 

skwee

Новичок
модель, только она знает структуру данных
А как быть с данными которые не идут в бд? Ну например captcha..

Поясни.

Только модель.
А разве модель должна создавать другие модели? (Смотри пример с ActivationCode)

Акшен это то что написано тут http://en.wikipedia.org/wiki/Command_pattern ?
 

fixxxer

К.О.
Партнер клуба
модель, только она знает структуру данных
Мой любимый контрпример - двойной ввод пароля при регистрации. Короткий ответ на изначальный вопрос - оба.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
fixxxer это никакой не контр-, а самый обычный пример
только модель знает смысл значения поля, что оно содержит, как его проверять (по значению другого поля) и что с ним потом сделать :)
ты считаешь иначе?

skwee
загугли
А как быть с данными которые не идут в бд?
http://zendframework.ru/anonses/model-with-mvc
модель - не база, модель может работать с базой, а может и не с базой, а со вводом пользователя

Акшен это то что
чаще всего это или метод в классе контроллера, или отдельный класс, а иногда и без объектов - просто скрипт, суть в смысле,
 

A1x

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

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Если следовать DDD, то если я правильно помню рекоммендации, то
созданием объекта юзера должна заниматься некая отдельная фабрика. Фабрика производит только валидный объект. Если фабрика не может создать валидный объект, она как-то сообщает об этом.
Должен существовать репозиторий юзеров, который работает уже с объектами, никаких сырых массивов из пост и т.д. В этот репозиторий мы сохраняем созданный фабрикой объект. В нем же ищем нужные объекты, обновляем их и т.д. Репозиторий общается с базой через какие-то другие простые объекты, которые умеют вставлять в таблицу и т.д.
Профит в том, что логика работы репозитория не захламлена деталями работы с бд и не зависит от способа создания объекта (форма регистрации, добавление из админки, из хитрого excel файла т.д.)
 

Духовность™

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

посмотри как у меня:
контроллер создания пользователя в админке: http://krugozor.svn.sourceforge.net/viewvc/krugozor/Krugozor/Module/User/Controller/BackendEdit.php?revision=46&content-type=text/plain
проверка логина на занятость:
PHP:
 if ($this->user->getLogin())
        {
            $validator->add('login', new Krugozor_Module_User_Validator_UserLoginExists(
                $this->user, $this->getMapper('User/User'))
            );
        }
Krugozor_Module_User_Validator_UserLoginExists знает о модели посредством передачи инстанса датамаппера ($this->getMapper('User/User'))
вот его код: http://krugozor.svn.sourceforge.net/viewvc/krugozor/Krugozor/Module/User/Validator/UserLoginExists.php?revision=20&content-type=text/plain
т.е. задача этого слоя валидации - вызвать метод findByParams() модели User и если что-то найдено, вернуть false - как символ ошибки
 

skwee

Новичок
grigori
я знаю что такое актив рекорд, но не понял как он переменим в данной ситуации.

модель - не база, модель может работать с базой, а может и не с базой, а со вводом пользователя
Я согласен, но с таким понятием для каждого метода создания юзера у меня будет своя модел с своим набором валидации либо МЕГА-модел которая покрывает все случаи. Не нравится мне это...

A1x
я создаю разные (перекрывающиеся) наборы правил валидации для разных контекстов и передаю имя контекста как параметр при валидации
ну я так понимаю это и есть "слой валидации".

varan
Очень красиво! Мне нравится! Надо почитать по глубже про DDD, у меня кстати сейчас так и есть (ну не совсем но рождается чтото похожее)

Духовность™
модель будет вызываться в слое валидации и лезть в базу
Как раз так и думал!

не надо эту проверку туда сувать
Почему нет? а если не получилось создать я же должен как то сказать экшену\контроллеру об этом.

Спасибо за пример!

Absinthe
Ну так форма - тоже модель.
Смотри мой ответ grigori
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Вообще говоря, модель - это как бы упрощенное понимание реального мира, набор идей. С помощью этих упрощений мы пишем код и решаем задачи бизнеса. Знание о том, что пароли должны совпадать - вполне может быть частью модели. А вот, например, цвет кнопки на этой форме уже навряд ли, ибо бессмысленно загромождает модель.
 

skwee

Новичок
Я тут про DDD почитал. Звучит заманчиво, оказывается я уже начал приходить к нему сам (ввел объект сущности с поведением), есть всякие аспекты которые я не совсем понимаю. Но походу именно это мне и надо. Но все же интересно кто занимается валидацие (фабрика? репозиторий? сама сущность?) И как быть с User-ом и ActivationCode-ом, так:
PHP:
$user = new UserFactory::create($data);
$aCode = new ActivationCodeFactory::create($data);
$user->setActivationCode($aCode);
(new UserRepository())->persist($user);
Но в таком случае кто сохраняет код активации?
или может так:
PHP:
$user = UserFactory::create($data);
(new UserRepository())->persist($user);
===============
class User extends Entity{
    public function __construct(...){
        $this->_activationCode = ActivationCodeFactory::create(...);
    }
}
а тут кто сохраняет код активации?

Вот так всегда, почитал - увидел в голове всю картину а как начинаешь писать код ни хрена не понятно(((
 

Absinthe

жожо
Форма не может быть моделью. Форма - это форма. Данные из формы могут стать данными модели.
аналогия "Авианосец не может быть транспортом. Авианосец - это авианосец. Самолет из авианосца может стать транспортом."

Форма - модель. Запись - тоже модель. Это 2 разных вида моделей.
 

Духовность™

Продвинутый новичок
аналогия "Авианосец не может быть транспортом. Авианосец - это авианосец. Самолет из авианосца может стать транспортом."

Форма - модель. Запись - тоже модель. Это 2 разных вида моделей.
// Модель - это то, из чего состоит программа, это описание ключевой сущьности.
// Форма - это лишь способ передать в модель данные. Данные в модель можно передать как из формы, так и из командной строки или с помощью телепортации.
// Аналогия - автомобиль и запрака. Считать заправочный шланг моделью - это абсурдно. Это чисто способ передать бензин в машину.
//Мы можем бензин залить так же с помощью канистры или бутылки.
 
Сверху