ZF: использование Zend Form отдельно от Zend Framework

zf 80 ZF: использование Zend Form отдельно от Zend Framework zend framework Большая часть классов и пакетов, входящих в состав Zend Framework, может быть использована отдельно от фреймворка в целом, и набор классов Zend_Form не исключение. Это самостоятельный компонент, отлично поддающийся настройке. Но расплачиваться за это придется тем, что очевидные и простые вещи на первый взгляд придется реализовывать через ж довольно нетривиально с подключением декораторов (Decorators) и помощников вида (View Helpers). Сложность разработки усугубляется еще и тем, что для одной задачи конечно же найдется несколько различных способов ее решения, порой сильно друг от друга отличающихся.

Вообще формы на вебе без преувеличения являются основным посредником «общения» между пользователем и приложением. Получается так, что форма соединяет в себе несколько частей приложения: клиентский код html и таблицы стилей, клиентские скрипты на javascript, обрабатывающие введенные пользователем данные на ранней стадии, и собственно саму обработку данных на стороне сервера. В серверную обработку входит фильтрация, валидация данных и их сохранение. В статье описаны следующие приемы:

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

Zend_Form в первом приближении

Различные части компонента связаны между собой:

Использование Zend_Form вне MVC

Zend_Form через декораторы использует помощника вида. Внутри фреймворка класс может получить доступ к виду Zend_View самостоятельно. Вне фреймворка, при необходимости использования стандартных декораторов, экземпляр класса Zend_View придется вручную скормить классу формы.

$view = new Zend_View();
$view->doctype('XHTML1_TRANSITIONAL');
$form = new Zend_Form();
$form->setView(new Zend_View());

В этом примере доктайп проставляется в представлении (виде). Влияет на рендеринг «самозакрывающихся» тегов input в xhtml (которые должны заканчиваться на />).

Разделение структуры и представления

Создадим форму для интерфейса администратора, в котором можно отредактировать информацию о пользователе. Предположим, что согласно ACL, некоторые админы могут редактировать профили пользователей, другие же только просматривать. Структура формы сведена к минимуму, а за конечное отображение отвечает в основном представление (вид).

Следуя правилам хорошего тона замусоривать контроллеры кодом, отвечающим за создание формы не нужно. Лучше всего унаследовать свой собственный класс формы от Zend_Form, а итоговый объект в контроллере создавать как экземпляр полученного класса — подкласса Zend_Form.

class My_Form_User extends Zend_Form
{
    public function init() {
        // username
        $this->addElement('text','username', array(
            'required' => true,
            'validators' => array(
                // arguments: type, breakchain, validator constructor options
                array('Alnum', false, false),
                array('StringLength', false, array(6, 16)),
            ),
        ));

        // email
        $EmailValidate = new My_Validator_Email();
        $this->addElement('text','email', array(
            'required' => true,
            'validators' => array(
                array('EmailAddress', false),
                array($EmailValidate, false)
            )
        ));
        // ...
    }
}

В коде создается экземпляр собственного валидатора My_Validator_Email(), который присоединяется к элементу формы email. Вернемся к этому чуть позже.

Метод init() устроен так, что нет необходимости дублировать конструктор класса и его параметры. Из примера понятно, что создаются только элементы формы и валидаторы. Надписи к полям будут задаваться в виде.

Теперь нужно создать два класса для рендеринга для двух режимов — просмотра и редактирования. Конструкторы обоих классов принимают в качестве параметра объект Zend_Form. Отображение формы с использованием каждого из классов происходит следующим образом:

$Form = new My_Form_User();

$RendererEdit = new My_Form_Renderer_Edit($Form, 'user_edit');
echo $RendererEdit->render();
$RendererView = new My_Form_Renderer_View($Form, 'user_view');
echo $RendererView->render();

Но перед тем как углубиться в рендеринг, вернемся к упомянутому классу My_Email_Validator.

Добавляем в форму свой валидатор

Если валидатор добавляется в форму по названию и описан строкой, например, «EmailAddress», класс Zend_Form автоматически добавляет один из стандартных валидаторов на основе Zend_Validate. Если стандартных валидаторов фреймворка не хватает, можно создать собственный, унаследовав Zend_Validate_Abstract, или же на основе Zend_Validator_Interface. При добавлении email или логинов на сайте необходимо удостовериться, что они еще не использовались в базе данных. Несмотря на то, что для подобных случаев в ZF уже есть необходимые валидаторы Zend_Validate_Db_RecordExists и Zend_Validate_Db_NoRecordExists, мы в качестве примера напишем свой валидатор. Опустим часть кода, отвечающую за соединение с базой данных и выборку.

class My_Validator_Email extends Zend_Validate_Abstract
{
    protected $isValid = null;

    public function isValid($value) {
        $this->isValid = !in_array($value, array(
            'duplicate@test.com',
        ));
        return $this->isValid;
    }

    public function getMessages() {
        if ($this->isValid === false) {
            return array(
                'duplicateEmail' => 'This email address is allready used'
            );
        }
        return array();
    }
    public function getErrors() {
        return array_keys($this->getMessages());
    }
}

Вернемся к классам рендеринга.

Отображение в режиме редактирования

Конструктор класса My_Form_Renderer_Edit:

class My_Form_Renderer_Edit
{
    protected $form;

    protected $lang;

    public function __construct(Zend_Form $form, $form_id = null)
    {
        $view = new Zend_View();
        $view->doctype('XHTML1_TRANSITIONAL');

        $this->form = $form;
        $this->form->setView(new Zend_View());
        $this->form->setAttrib('class', 'form_edit');
        if (!is_null($form_id)) {
            $this->form->setAttrib('id', $form_id);
        }

        $this->lang = new My_LanguagePack();
    }
    public function render()
    {
        // ...
}

Здесь настраивается вид (отображение), а также присваиваются атрибуты id и class, которые можно использовать в таблице стилей. HTML будет выглядеть следующим образом:

  • (Min 6, max 16 char.)

При вызове метода render() происходит следующее:

Подробнее можно посмотреть в исходном коде.

Кнопка «Отправить»

Поскольку кнопка отправки сама по себе в большинстве случаев никак не влияет на обработку данных из формы, целесообразно запихнуть ее в вид.

Настройка общих свойств элементов

При помощи метода setElementDecorators() класса Zend_Form можно переназначить всем элементам один и тот же декоратор вместо стандартных. Нам подойдут ViewHelper (рендерит саму форму) и Description.

protected function setupElementsCommon()
    {
         $this->form->setElementDecorators(array(
            'ViewHelper',
            array('Description', array(
                'placement' => 'append',
                'tag' => 'span',
                'class' => 'description'
            )),
         ));
    }

Настраиваем свойства каждого элемента в отдельности

Здесь мы добавим дополнительные декораторы к элементам. У автора оригинала статьи используется языковой файл для переопределения надписей к элементам, но нам это сейчас не нужно. Если при обработке данных для элемента валидатор выдает ошибку, мы добавим для данного элемента дополнительный класс, который будем использовать позже:

        $elmHasError = (count($elm->getMessages()) > 0);
        $liClass = $elmHasError ? 'error' : '';
        $elm->addDecorator(
            array('outerLi' => 'HtmlTag'),
            array('tag' => 'li', 'class' => $liClass)
        );

Настраиваем объект формы

Форма настроена. Отличия от основных декораторов в том, что для декоратора HtmlTag в качестве тэга используется UL.

    protected function setupForm()
    {
        $this->form->clearDecorators();
        $this->form->addDecorator('FormElements');
        $this->form->addDecorator('HtmlTag', array('tag' => 'ul'));
        $this->form->addDecorator('Form');
    }

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

Рендеринг в режиме просмотра

По сути это упрощенная версия представления для редактирования. Основные отличия:

My_Decorator_View_Element

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

Заключение

Примечательно, что Zend_Form вполне можно использовать в самописных приложениях, как и любые другие компоненты фреймворка. К сожалению, из-за «разношерстного» подхода к реализации частей любой системы стройность ее теряется.

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

Rambler's Top100