ТТУК - Толстые тупые уродливые контроллеры

Духовность™

Продвинутый новичок
ТТУК - Толстые тупые уродливые контроллеры

Прочитал статью http://zendframework.ru/anonses/model-with-mvc и заострил своё внимание на этом:
Среднестатистический ТТУК получал данные из БД (используя уровень абстракции базы данных, делая вид, что это модель) или манипулировал, валидировал, записывал, а также передавал данные в Вид. Такой подход стал очень популярен, потому что использование таких контроллеров похоже на классическую практику использования отдельного php файла для каждой страницы приложения.
Что получается? Hrakk мне как-то сказал, что "я использую контроллер как элемент, управляющий логикой, а он должен говорить _что_ делать".
Отчасти я согласен, но уложиться в схему, когда в контроллере не образуется логики и Н-ного количества строк переваливающих за 50 лично у меня не получается. Вот скажите, это толстый контроллер: http://paste.org/pastebin/view/18126 или нет? Код данного недописанного (!!) контроллера просто занимается сохранением в базу объекта из POST-запроса (регистрация). Он толстый в терминологии автора статьи или нет?

Что вообще думаете по этому вопросу? Что должна делать модель? Что не должна делать модель? Что должно быть в контроллере, что бы он не был ТТУК?
 

zerkms

TDD infected
Команда форума
Я сейчас пришёл к варианту, когда к рядовым слоям MVC добавляется ещё один - сервисный слой (по факту там всё сложнее, но в общем виде именно так). Вот сервисный слой и содержит всю бизнес-логику.

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

-~{}~ 05.05.10 09:26:

Что должна делать модель?
должна-не должна... никто никому ничего не должен, каждый делает так, как удобнее

как я сказал выше - я пришёл к мнению, что модель максимум что должна уметь - это CRUD над хранилищем + базовые манипуляции/логика с данными (которая (логика) не может быть отнесена к бизнес-логике).
 

Духовность™

Продвинутый новичок
- контроллер отвечает за получение данных запроса (для передачи их аргументами в сервисный слой) и за получение результатов работы сервисного слоя и реакции на них (редирект, передача во view, ...)
понятно. одно не понятно - зачем для этого городить ещё один слой, если можно благополучно это все делать в контроллере? Ведь наверняка получаются ТТУС - Толстые тупые уродливые сервисы, предназначенные конкретно для конкретных контроллеров и наверняка усложняющие задачу.

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

я пришёл к мнению, что модель максимум что должна уметь - это CRUD над хранилищем + базовые манипуляции/логика с данными (которая (логика) не может быть отнесена к бизнес-логике).
угу, согласен насчет CRUD. а бизнес-логика - это что такое в твоем понимании?
 

zerkms

TDD infected
Команда форума
понятно. одно не понятно - зачем для этого городить ещё один слой, если можно благополучно это все делать в контроллере?
затем, что:
1. бизнес теперь будет сосредоточен в одном месте
2. теперь можно будет писать разные морды вокруг одного сервисного слоя

Ведь наверняка получаются ТТУС
не получаются. ты не переносишь весь код из контроллеров в сервис. сервисы теперь занимаются только бизнесом, а контроллеры (как и должны) теперь glue layer, склеивающий остальные слои.

а бизнес-логика - это что такое в твоем понимании?
бизнес - это собственно то, ради чего задумывается приложение.


ну вот давай навскидку пример:

PHP:
// controller
public function saveNews()
{
    $result = $this->newsService->save($_POST);
    
    if ($result->success)
    {
        return $this->redirect();
    }

    $this->view->errors = $result->errors;
    return $this->view;
}

// service layer, newsService
public function save(array $data)
{
    // validation
    if ($invalid)
    {
        return new result(false, $errors);
    }

    // data modification in persistent storage
    return new result(true);
}
 

HraKK

Мудак
Команда форума
Я могу как бы показать:
PHP:
protected function index()
    {
        $Uri = $this->getApplication()->getRequest()->getUri();
        $idShop = $Uri->current();
        $idService = $Uri->next();
        if( !$idShop || !$idService )
        {
            $this->redirect( '/admin/shops' );
        }

        $Shop = Modules_Shops_Collection_Shop::getInstance()->addShop();
        $Shop->id = $idShop;
        if( $Shop->id_user != Core_User::getInstance()->getUserID() )
        {
            $this->redirect( '/admin/shops' );
        }

        $allow = false;
        foreach ( $Shop->getServices() as $Service )
        {
           if( $Service->id_service == $idService )
           {
               $allow = true;
               break;
           }
        }

        if( !$allow )
        {
            $this->redirect( '/admin/shops' );
        }

        $controllerClass = 'Modules_Admin_Controller_Services_' . ucfirst( strtolower( $Service->alias ) );
        $Controller = new $controllerClass;
        return $Controller->dispatch();
    }
Тут мне надо отобразить конкретный сервис, у магазина у биллинга.
Страница запроса имеет вид типа /shop/services/1/2
где 1 это ид магазина, 2 - ид сервиса.

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

Но при этом мой контроллер не заботится о логике и модели сервисов.

А заботиться только о разборе полученной извне информации и о том ЧТО с ней делать и как ее выводить, а как ее обрабатывать забота уже не контроллера.
 

Fortop

Новичок
Вот скажите, это толстый контроллер: http://paste.org/pastebin/view/18126 или нет? Код данного недописанного (!!) контроллера просто занимается сохранением в базу объекта из POST-запроса (регистрация). Он толстый в терминологии автора статьи или нет?
Почему бы не спросить автора статьи? :D

На самом деле, надо вынести свой валидатор, например, из метода post() в форму, сервисный слой или просто в Module_User_Mapper_User. И контроллер похудеет сразу на 40 строк

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

fixxxer

К.О.
Партнер клуба
>>Я сейчас пришёл к варианту, когда к рядовым слоям MVC добавляется ещё один - сервисный слой (по факту там всё сложнее, но в общем виде именно так). Вот сервисный слой и содержит всю бизнес-логику.


о, ну хоть еще кто-то.

я про это год назад безуспешно втирал тут :D

-~{}~ 05.05.10 06:09:

хотя может и не про это. вот например редактирование профайла юзера как у тебя бы выглядело? ;)
 

zerkms

TDD infected
Команда форума
fixxxer
эм... ну наверное как-то вот так:

PHP:
// profile controller
public function edit($id)
{
    $result = $this->profileService->edit($id, $_POST);
    // выбираем вьюшку или что-то ещё делаем, в зависимости от содержимого $result
}
ы? ;-)
 

cDLEON

Онанист РНРСlub
zerkms
В чём смысл переносить кусок кода из одного контроллера, в сервис, который, по-сути, является всё тем же контроллером ?
 

fixxxer

К.О.
Партнер клуба
ы.

но в таком виде не совсем понятно.

как бы обычно еще есть валидация-обработка ошибок, в зависимости от результата - редирект/отображение формы заново

ну и форма в каком то виде:)

еще момент - обработка ошибок, зависящих от модели (например смена email + email varchar unique)
 

zerkms

TDD infected
Команда форума
cDLEON
в том, что этот кусок - бизнес-логика, которая может быть использована многократно в разных контроллерах (клиентах).
а в контроллере останется только логика, присущая контроллеру: т.е. выбрать сервис, передать результаты в нужную вьюшку, запустить, итд

-~{}~ 05.05.10 14:08:

fixxxer
ну в первом варианте всё есть - и валидация, и редирект, и форма.
 

fixxxer

К.О.
Партнер клуба
а. я комментарий не прочитал :)

ну у меня немного не так, хотя похоже. все таки я кое что в контроллере оставил =)
PHP:
class UserProfilePage extends WebPage {

    public function run() {
        $Profile = Profile::construct($this->CurrentUser);
        $Form = ProfileForm::construct()->fetchFrom($Profile);
        if ($this->Request->post() && $Form->fetchFrom($this->Request->Vars)->validate()) {
            $Profile->fetchFrom($Form)->save()
               and $this->Response->redirectToPage($this);
        }
        $this->View->Form->bind($Form);
    }

}
 

zerkms

TDD infected
Команда форума
fixxxer
ну твой код не противоречит моим словам в принципе.

PHP:
class UserProfilePage extends WebPage {

    public function run() {
        $Profile = Profile::construct($this->CurrentUser); // эта строка тут - потому что нам нужен профиль, чтобы передать его в сервис
        $Form = ProfileForm::construct()->fetchFrom($Profile); // форма, исключительно view, сервис ничего о ней и не знает даже
        if ($this->Request->post() && $Form->fetchFrom($this->Request->Vars)->validate()) { // вот разве что эта строку я немного иначе сделал бы: получается, что у тебя форма должна знать и бизнес-правила, по которым нужно валидировать: например, при смене email мы не должны иметь возможность сменить его на уже существующий
            $Profile->fetchFrom($Form)->save(); // привет, "сервис"
            $this->Response->redirectToPage($this); // редирект, опять же задача сугубо контроллера
        }
        $this->View->Form->bind($Form); // вызов вьюшки, тоже задача контроллер
    }

}
 

fixxxer

К.О.
Партнер клуба
>> вот разве что эта строку я немного иначе сделал бы: получается, что у тебя форма должна знать и бизнес-правила, по которым нужно валидировать: например, при смене email мы не должны иметь возможность сменить его на уже существующий

А я потому и спрашивал детали, гы-гы. У меня вот единственное место, которое мне не нравится, - что валидация размазана.

Наверное, надо валидаторы отвязать от полей именно формы, и сделать свойством entities. Типа Form::constructByEntity($Profile).
 

zerkms

TDD infected
Команда форума
Наверное, надо валидаторы отвязать от полей именно формы, и сделать свойством entities. Типа Form::constructByEntity($Profile).
почему не как я предложил - просто взять и перенести валидацию в сервис?
 

fixxxer

К.О.
Партнер клуба
Но ведь есть и проверки, которым место как раз тут - например двойной ввод пароля при регистрации ) "Сервису" то про это вообще знать нефиг, по идее.
 

zerkms

TDD infected
Команда форума
fixxxer
кстати да %) сам же в недавнишем очередном споре про MVC приводил аналогичный пример с подтверждением пароля %)
 

fixxxer

К.О.
Партнер клуба
мм....

PHP:
$Form = Form::constructByEntity($Profile);
$Form
    ->addFields(array(
        'password2' => new Field_Scalar
    ))
    ->addConstraints(array(
        'password2' => new Rule_EqualsToValueOf($Form->password)
    ))
...
хм хм. может так.
 

Fortop

Новичок
fixxxer
Типовые формы - это редкость.
В конечном счете проще указать все необходимые валидаторы для формы - руками.

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