4 февраля 2013 г.

Использование Zend_Form в Моделях

В последнее время в сообществе Zend Framework (ZF) выросло количество постов посвященных обсуждению Модели в шаблоне Модель-Вид-Контроллер. В ZF нет конкретного класса или интерфейса Модели; авторы фреймворка придерживаются позиции, что модели для каждого приложения специфичны и только разработчик может точно определить наиболее подходящий вариант. Многие другие фреймворки привязывают Модель к шаблонам доступа к данным - как правило: ActiveRecord или Table Data Gateway, не обращая внимания на тот факт, что Модель привязывается к способу сохранения данных. 
А что произойдет, если Вы начнете использовать Memcached? или перейдете на архитектуру SOA? Что, если с самого начала Ваши данные поступают из веб-сервиса? Что делать, если вы используете базу данных, но бизнес-логика опирается на связи между таблицами? 
Замечательные публикации, которые упомянуты выше, обсуждают различные подходы (используемые при создании модели), но не обязательно указывают на какие-либо конкретные подходы, которые разработчик может использовать при создании своих моделей. Таким образом, это будет первый пост из серии публикаций о некоторых конкретных методах, которые можно использовать при создании моделей. Подходы будут опираться на компоненты ZF, но могут быть использованы и для ряда других фреймворков. 

Формы и фильтрация входных данных

В большинстве случаев программист хочет, чтобы Модель самостоятельно выполняла фильтрацию входящих данных. Причина в том, что фильтрация ввода принадлежит доменной логике (бизнес логике): это набор правил, которые определяют валидность входящих данных и привила их нормализации.
Однако, как это согласуется с формами? ZF имеет компонент Zend_Form, который позволяет использовать как цепочки валидаторов и фильтров для проверки входных данных, так и декораторы, определяющие внешний вид. Типичным подходом является создание Формы в Контроллере и передача входящих данных в нее; если данные проходят валидацию, они далее передаются в Модель.
А что, если привязывать Форму к Модели? Некоторые возразят, что это нарушает понятие "разделение ответственности" в связи с тем, что логика отображения Формы примешивается к Модели. Однако, по мнению Matthew O'Phinney, такая забота о чистоте кода является слишком педантичной. При подключении к Модели Форма может быть использована исключительно в качестве входного фильтра; когда же возникнут все специфичные для логики отображения действия, например, конфигурирование декораторов, установка атрибутов action и method и т.д. они выполняются через скрипт Вида. Кроме того, различные плагины такие, как - валидаторы, фильтры или декораторы - не загружается до их использования - то есть, практически, нет накладных расходов, при использовании Форм в качестве фильтра входящих данных. 
Такой подход поможет придерживаться принципа DRY, одновременно помогая поддерживать четкое разделение бизнес логики и отображения. Наконец, можно вызывать как одну, так и несколько Форм из Модели, что поспособствует быстрой разработке приложений, а также обеспечит прочную семантическую связь между моделью и представлением. Перейдем непосредственно к методике. 

Прикрепление Форм к Моделям 

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

class Spindle_Model_Bug
{
    protected $_forms = array();

    public function getForm($type = 'bug')
    {
        $type  = ucfirst($type);
        if (!isset($this->_forms[$type])) {
            $class = 'Spindle_Model_Form_' . $type;
            $this->_forms[$type] = new $class;
        }
        return $this->_forms[$type];
    }

    public function save(array $data)
    {
        $form = $this->getForm();
        if (!$form->isValid($data)) {
            return false;
        }

        $storage = $this->getStorage();
        if ($form->getValue('id')) {
            $id = $form->getValue('id');
            $storage->update($form->getValues(), $id));
        } else {
            $id = $storage->insert($form->getValues());
        }

        return $id;
    }
}

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

class BugController
{
    public function processAction()
    {
        $request = $this->getRequest();
        if (!$request->isPost()) {
            return $this->_helper->redirector('new');
        }

        if (!$id = $this->model->save($request->getPost())) {
            // Failed validation; re-render form page
            $this->view->model = $model;
            return $this->render('new');
        }

        // redirect to view newly saved bug
        $this->_helper->redirector('view', null, null, array('id' => $id));
    }
}

Как видите, в Контроллере немного кода и вообще не упоминаются формы. Итак, как мы будем отображать Форму? Заметим, что Модель передается в Вид - что в конечном итоге дает нам доступ к Форме. 

$form = $this->model->getForm();
$form->setMethod('post')
     ->setAction($this->url(array('action' => 'process')));
echo $form;

Такой подход позволяет разделить зоны ответственности. Вы выводите форму, которая дальше будет использоваться для фильтрации данных текущей модели. Обратите внимание на логику отображения, помещенную в файл Вида. Здесь задаются параметры method и action для текущей Формы, что как раз соответствует логике отображения. 

Резюме 

 Есть, конечно, и другие способы решения проблемы, но это удобное и выгодное решение, которое максимизирует использование существующих компонентов. Привязка Форм к Моделям помещает всю логику, связанную с проверкой входных данных - в том числе сообщений об ошибках - в одном месте, и гарантирует, что Ваши формы останутся актуальными в случае изменения модели, например правил валидации или списка разрешенных входных данных.(перевод поста Matthew O'Phinney.)

Комментариев нет:

Отправить комментарий