<sect1 id="zend.form.quickstart">
    <title>Zend_Form - Быстрый старт</title>

    <para>
        Данное руководство охватывает основы создания форм,
        проверки корректности данных и визуализиции с использованием
        <code>Zend_Form</code>.
    </para>

    <sect2 id="zend.form.quickstart.create">
        <title>Создание объекта формы</title>

        <para>
            Объекты форм создаются через простое инстанцирование
            <code>Zend_Form</code>:
        </para>

        <programlisting language="php"><![CDATA[
$form = new Zend_Form;
]]>
        </programlisting>

        <para>
            Для более сложных случаев использования вы можете создавать
            подклассы <code>Zend_Form</code>, но простые формы вы можете
            создавать, используя объект <code>Zend_Form</code>.
        </para>

        <para>
            Если вы хотите указать атрибуты <code>action</code> и
            <code>method</code> (что во всех случаях является хорошей идеей), то
            можете сделать это с использованием аксессоров
            <code>setAction()</code> и <code>setMethod()</code>:
        </para>

        <programlisting language="php"><![CDATA[
$form->setAction('/resource/process')
     ->setMethod('post');
]]>
        </programlisting>

        <para>
            Приведенный выше код устанавливает значение атрибута
            <code>action</code> равным "/resource/process" и указывает способ
            отправки данных - HTTP POST. Эти атрибуты будут выведены после
            окончательного рендеринга формы.
        </para>

        <para>
            Вы можете установить дополнительные HTML-атрибуты для тега
            <code>&lt;form&gt;</code>, используя методы <code>setAttrib()</code>
            и <code>setAttribs()</code>. Например, если нужно установить
            идентификатор элемента формы, то установите атрибут "id":
        </para>

        <programlisting language="php"><![CDATA[
$form->setAttrib('id', 'login');
]]>
        </programlisting>
    </sect2>

    <sect2 id="zend.form.quickstart.elements">
        <title>Добавление элементов в форму</title>

        <para>
            Форма без элементов бесмысленна. <code>Zend_Form</code>
            поставляется с некоторым начальным набором элементов, которые
            отвечают за рендеринг XHTML-кода с использованием помощников
            <code>Zend_View</code>. В этот список входят элементы:
        </para>

        <itemizedlist>
            <listitem><para>
                button (кнопка)
            </para></listitem>

            <listitem><para>
                checkbox (флажок опций, или несколько флажков опций
                через multiCheckbox)
            </para></listitem>

            <listitem><para>
                hidden (спрятанное поле)
            </para></listitem>

            <listitem><para>
                image (изображение)
            </para></listitem>

            <listitem><para>
                password (пароль)
            </para></listitem>

            <listitem><para>
                radio (переключатель)
            </para></listitem>

            <listitem><para>
                reset (кнопка сброса)
            </para></listitem>

            <listitem><para>
                select (выпадающий список - как обычный, так multi-select)
            </para></listitem>

            <listitem><para>
                submit (кнопка отправки)
            </para></listitem>

            <listitem><para>
                text (текстовое поле)
            </para></listitem>

            <listitem><para>
                textarea (текстовая область)
            </para></listitem>
        </itemizedlist>

        <para>
            Есть два способа добавления элементов в форму - вы можете
            инстанцировать нужные элементы и передавать их объекту формы, или
            передавать только тип элемента, в этом случае <code>Zend_Form</code>
            инстанцирует соответствующий объект за вас.
        </para>

        <para>
            Некоторые примеры:
        </para>

        <programlisting language="php"><![CDATA[
// Инстанцирование элемента и его передача объекту формы:
$form->addElement(new Zend_Form_Element_Text('username'));

// Передача типа элемента объекту формы
$form->addElement('text', 'username');
]]>
        </programlisting>

        <para>
            По умолчанию элементы не имеют никаких валидаторов или фильтров. Это
            означает, что вам нужно установить к своим элементам, как минимум,
            валидаторы, и, возможно, фильтры. Вы можете делать это (a) до
            передачи элементов в форму, (b) через опции конфигурирования,
            которые передаются при создании элемента через
            <code>Zend_Form</code>, или (с) путем извлечения элементов формы из
            объекта формы и их конфигурирования.
        </para>

        <para>
            Сначала рассмотрим создание валидаторов для конкретного объекта
            элемента. Вы можете передавать объекты <code>Zend_Validate_*</code>
            или имена валидаторов:
        </para>

        <programlisting language="php"><![CDATA[
$username = new Zend_Form_Element_Text('username');

// Передача объекта Zend_Validate_*:
$username->addValidator(new Zend_Validate_Alnum());

// Передача имени валидатора:
$username->addValidator('alnum');
]]></programlisting>

        <para>
            В случае использования второго варианта, если валидатор принимает
            аргументы конструктора, то вы можете передавать их через массив как
            третий параметр:
        </para>

        <programlisting language="php"><![CDATA[
// Передача шаблона
$username->addValidator('regex', false, array('/^[a-z]/i'));
]]>
        </programlisting>

        <para>
            (Второй параметр используется для указания того, должен ли валидатор
            в том случае, если данные не прошли проверку, прерывать дальнейшую
            проверку в цепочке валидаторов; по умолчанию он равен false.)
        </para>

        <para>
            Вы можете также указать элемент как обязательный для заполнения. Это
            может быть сделано как с помощью аксессора, так и путем
            передачи определенной опции при создании элемента. В первом случае:
        </para>

        <programlisting language="php"><![CDATA[
// Указание того, что элемент обязателен для заполнения:
$username->setRequired(true);
]]>
        </programlisting>

        <para>
            Если элемент обязателен для заполнения, то в начало цепочки
            валидаторов добавляется валидатор 'NotEmpty', который проверяет,
            имеет ли элемент значение.
        </para>

        <para>
            Фильтры регистрируются в основном так же, как и валидаторы. Для
            демонстрации добавим фильтр для приведения значения к
            нижнему регистру:
        </para>

        <programlisting language="php"><![CDATA[
$username->addFilter('StringToLower');
]]>
        </programlisting>

        <para>
            Таким образом, окончательно установка элемента получится
            такой, как показано ниже:
        </para>

        <programlisting language="php"><![CDATA[
$username->addValidator('alnum')
         ->addValidator('regex', false, array('/^[a-z]/'))
         ->setRequired(true)
         ->addFilter('StringToLower');

// или в более компактной форме:
$username->addValidators(array('alnum',
        array('regex', false, '/^[a-z]/i')
    ))
    ->setRequired(true)
    ->addFilters(array('StringToLower'));
]]>
        </programlisting>

        <para>
            Выполнение этих действий для каждого элемента по отдельности может
            показаться несколько утомительным. Попробуем вариант (b) из
            перечисленных выше. Когда мы создаем новый элемент, используя
            <code>Zend_Form::addElement()</code> в качестве фабрики, то можем
            опционально передавать опции конфигурирования. Они могут включать в
            себя валидаторы и фильтры для использования. Таким образом, чтобы
            неявным образом сделать все это, попробуйте следующее:
        </para>

        <programlisting language="php"><![CDATA[
$form->addElement('text', 'username', array(
    'validators' => array(
        'alnum',
        array('regex', false, '/^[a-z]/i')
    ),
    'required' => true,
    'filters'  => array('StringToLower'),
));
]]>
        </programlisting>

        <note><para>
            Если вы обнаружили, что настраиваете элементы, используя одни и те
            же опции во многих местах, то можете создать подкласс
            <code>Zend_Form_Element</code> и использовать его вместо выполнения
            этих процедур; это может избавить от лишней работы по набору кода.
        </para></note>
    </sect2>

    <sect2 id="zend.form.quickstart.render">
        <title>Визуализация формы</title>

        <para>
            Визуализация формы производится легко. Большинство элементов
            использует помощника <code>Zend_View</code> для генерации вывода и
            поэтому нуждаются в объекте вида для выполнения рендеринга. Есть
            два способа запустить рендеринг: использовать метод формы render()
            или просто вывести форму с помощью echo.
        </para>

        <programlisting language="php"><![CDATA[
// Явный вызов render() с передачей объекта вида:
echo $form->render($view);

// Предполагается, что объект вида уже был установлен ранее через setView():
echo $form;
]]>
        </programlisting>

        <para>
            По умолчанию <code>Zend_Form</code> будет пытаться использовать
            объект вида, инициализированный в <code>ViewRenderer</code>, это
            означает, что вам не нужно будет вручную устанавливать объект вида
            при использовании MVC Zend Framework-а. Код для визуализации формы
            в скрипте вида весьма прост:
        </para>

        <programlisting language="php"><![CDATA[
<?php echo $this->form ?>
]]>
        </programlisting>

        <para>
            Внутри себя <code>Zend_Form</code> использует "декораторы" для
            выполнения визуализации. Эти декораторы могут замещать содержимое
            переданного элемента, производить добавления в его начало и конец,
            производить наблюдение за ним. В результате вы можете комбинировать
            несколько декораторов для достижения нужного эффекта. По умолчанию
            в <code>Zend_Form_Element</code> используется четыре декоратора
            для получения нужного вывода; их установка выглядит приблизительно
            так:
        </para>

        <programlisting language="php"><![CDATA[
$element->addDecorators(array(
    'ViewHelper',
    'Errors',
    array('HtmlTag', array('tag' => 'dd')),
    array('Label', array('tag' => 'dt')),
));
]]>
        </programlisting>

        <para>
            Код выше создает вывод наподобие следующего:
        </para>

        <programlisting language="html"><![CDATA[
<dt><label for="username" class="required">Username</dt>
<dd>
    <input type="text" name="username" value="123-abc" />
    <ul class="errors">
        <li>'123-abc' has not only alphabetic and digit characters</li>
        <li>'123-abc' does not match against pattern '/^[a-z]/i'</li>
    </ul>
</dd>
]]>
        </programlisting>

        <para>
            Вы можете изменить набор декораторов, используемый элементом, если
            хотите иметь другой результат вывода; более подробную информацию
            читайте в разделе о декораторах.
        </para>

        <para>
            Форма сама по себе просто производит обход содержащегося в ней
            списка элементов и окружает получившийся вывод тегами
            <code>&lt;form&gt;</code>. Переданные вами <code>action</code> и
            <code>method</code> устанавливаются в качестве атрибутов тега
            <code>&lt;form&gt;</code> - так же, как и остальные атрибуты,
            установленные через семейство методов
            <code>setAttribs()</code>.
        </para>

        <para>
            Элементы обходятся в том же порядке, в котором они были
            зарегистрированы, но если ваш элемент содержит атрибут order, то он
            используется для сортировки. Вы можете установить порядок элемента,
            используя:
        </para>

        <programlisting language="php"><![CDATA[
$element->setOrder(10);
]]>
        </programlisting>

        <para>
            Или путем передачи в качестве опции при создании элемента:
        </para>

        <programlisting language="php"><![CDATA[
$form->addElement('text', 'username', array('order' => 10));
]]>
        </programlisting>
    </sect2>

    <sect2 id="zend.form.quickstart.validate">
        <title>Проверка корректности данных формы</title>

        <para>
            После того, как получены данные формы, нужно их проверить
            и выяснить, правильно ли заполнена форма. Для всех элементов
            производится проверка переданных
            данных на наличие ключа, соответствующего имени элемента. Если этот
            ключ не найден, и элемент при этом помечен как обязательный, то для
            проверки на корректность используется значение null.
        </para>

        <para>
            Откуда идут данные? Вы можете использовать <varname>$_POST</varname>,
            <varname>$_GET</varname>, и любые другие источники данных
            (например, запросы веб-сервисов):
        </para>

        <programlisting language="php"><![CDATA[
if ($form->isValid($_POST)) {
    // успех
} else {
    // неудача
}
]]>
        </programlisting>

        <para>
            Вам может прийти в голову идея проверять данные одного элемента или
            группы элементов с помощью AJAX-запросов.
            Метод <code>isValidPartial()</code> будет проверять на корректность
            данные части формы. Его отличие от <code>isValid()</code> состоит в
            том, что если в данных формы отсутствует какой-либо ключ, то для
            этого элемента не будут производиться проверки на корректность
            заполнения:
        </para>

        <programlisting language="php"><![CDATA[
if ($form->isValidPartial($_POST)) {
    // все предоставленные элементы прошли все проверки на корректность
} else {
    // один или более элементов не прошли проверку на корректность
}
]]>
        </programlisting>

        <para>
            Для проверки части формы может также использоваться метод
            <code>processAjax()</code>. В отличие от
            <code>isValidPartial()</code>, в случае неуспеха он возвращает
            строку в формате JSON, содержащую сообщения об ошибках заполнения.
        </para>

        <para>
            Если проверка на корректность заполнения была пройдена успешно,
            то вы можете извлечь прошедшие фильтрацию данные:
        </para>

        <programlisting language="php"><![CDATA[
$values = $form->getValues();
]]>
        </programlisting>

        <para>
            Для того, чтобы извлечь нефильтрованные данные, используйте:
        </para>

        <programlisting language="php"><![CDATA[
$unfiltered = $form->getUnfilteredValues();
]]>
        </programlisting>
    </sect2>

    <sect2 id="zend.form.quickstart.errorstatus">
        <title>Получение статуса ошибки</title>

        <para>
            А что в том случае, если форма не прошла проверку на корректность?
            Как правило, вы можете просто вывести ее снова, и сообщения
            об ошибках будут отображены, если вы используете
            декораторы по умолчанию:
        </para>

        <programlisting language="php"><![CDATA[
if (!$form->isValid($_POST)) {
    echo $form;

    // или присвойте ее объекту вида и произведите его рендеринг...
    $this->view->form = $form;
    return $this->render('form');
}
]]>
        </programlisting>

        <para>
            Если нужно проанализировать ошибки, то есть два способа их
            получения. <code>getErrors()</code> возвращает ассоциативный массив
            имен элементов и кодов ошибок (где коды ошибок представлены в виде
            массива). <code>getMessages()</code> возвращает ассоциативный массив
            имен элементов и сообщений об ошибках (где сообщения об ошибках
            представлены в виде ассоциативного массива пар 'код
            ошибки'/'сообщение об ошибке'). Если элемент не имеет ошибок, то он
            не будет включен в массив.
        </para>
    </sect2>

    <sect2 id="zend.form.quickstart.puttingtogether">
        <title>Объединяя изложенное</title>

        <para>
            Давайте создадим простую форму для входа на сайт. Для нее будут
            нужны следующие элементы:
        </para>

        <itemizedlist>
            <listitem><para>username</para></listitem>
            <listitem><para>password</para></listitem>
            <listitem><para>submit</para></listitem>
        </itemizedlist>

        <para>
            Для примера предположим, что корректное имя пользователя должно
            содержать только буквенно-цифровые символы, начинаться с буквы,
            иметь длину не меньше 6 и не больше 20 символов, кроме этого, имена
            пользователей должны быть приведены к нижнему регистру. Пароль
            должен содержать как минимум 6 символов. Переданное значение кнопки
            использоваться не будет, поэтому проверка для нее может не
            производиться.
        </para>

        <para>
            Мы используем мощь конфигурационных опций <code>Zend_Form</code> для
            построения формы:
        </para>

        <programlisting language="php"><![CDATA[
$form = new Zend_Form();
$form->setAction('/user/login')
     ->setMethod('post');

// Создание и конфигурирование элемента username
$username = $form->createElement('text', 'username');
$username->addValidator('alnum')
         ->addValidator('regex', false, array('/^[a-z]+/'))
         ->addValidator('stringLength', false, array(6, 20))
         ->setRequired(true)
         ->addFilter('StringToLower');

// Создание и конфигурирование элемента password
$password = $form->createElement('password', 'password');
$password->addValidator('StringLength', false, array(6))
         ->setRequired(true);

// Добавление элементов в форму:
$form->addElement($username)
     ->addElement($password)
     // addElement() используется в качестве "фабрики"
     // для создания кнопки 'Login':
     ->addElement('submit', 'login', array('label' => 'Login'));
]]>
        </programlisting>

        <para>
            Затем создается контроллер для отображения формы и ее обработки:
        </para>

        <programlisting language="php"><![CDATA[
class UserController extends Zend_Controller_Action
{
    public function getForm()
    {
        // здесь должен быть код для создания формы, приведенный выше
        return $form;
    }

    public function indexAction()
    {
        // рендеринг user/form.phtml
        $this->view->form = $this->getForm();
        $this->render('form');
    }

    public function loginAction()
    {
        if (!$this->getRequest()->isPost()) {
            return $this->_forward('index');
        }
        $form = $this->getForm();
        if (!$form->isValid($_POST)) {
            // проверка на корректность не пройдена, выводим форму снова
            $this->form = $form;
            return $this->render('form');
        }

        $values = $form->getValues();
        // аутентификация...
    }
}
]]>
        </programlisting>

        <para>
            ...и скрипт вида для отображения формы:
        </para>

        <programlisting language="php"><![CDATA[
<h2>Please login:</h2>
<?= $this->form ?>
]]>
        </programlisting>

        <para>
            Как вы наверное заметили, код контроллера не является полным -
            после успешно проведенной проверки должна производиться авторизация
            пользователя (например, используя <code>Zend_Auth</code>).
        </para>
    </sect2>

    <sect2 id="zend.form.quickstart.config">
        <title>Использование объекта Zend_Config</title>

        <para>
            Все классы <code>Zend_Form</code> можно конфигурировать, используя
            <code>Zend_Config</code>. Вы можете передавать объект
            <code>Zend_Config</code> либо конструктору, либо через метод
            <code>setConfig()</code>. Посмотрим, как можно создать описанную
            выше форму, используя файл INI. Во-первых, будем следовать
            рекомендации размещать конфигурации в разделах, отражающих
            местонахождение релиза, и сфокусируемся на разделе 'development'.
            Во-вторых, установим раздел для данного контроллера ('user') и
            ключ для формы ('login'):
        </para>

        <programlisting language="ini"><![CDATA[
[development]
; общие метаданные для формы
user.login.action = "/user/login"
user.login.method = "post"

; элемент username
user.login.elements.username.type = "text"
user.login.elements.username.options.validators.alnum.validator = "alnum"
user.login.elements.username.options.validators.regex.validator = "regex"
user.login.elements.username.options.validators.regex.options.pattern = "/^[a-z]/i"
user.login.elements.username.options.validators.strlen.validator = "StringLength"
user.login.elements.username.options.validators.strlen.options.min = "6"
user.login.elements.username.options.validators.strlen.options.max = "20"
user.login.elements.username.options.required = true
user.login.elements.username.options.filters.lower.filter = "StringToLower"

; элемент password
user.login.elements.password.type = "password"
user.login.elements.password.options.validators.strlen.validator = "StringLength"
user.login.elements.password.options.validators.strlen.options.min = "6"
user.login.elements.password.options.required = true

; элемент кнопки
user.login.elements.submit.type = "submit"
]]></programlisting>

        <para>
            Вы можете потом передать это конструктору формы:
        </para>

        <programlisting language="php"><![CDATA[
$config = new Zend_Config_Ini($configFile, 'development');
$form   = new Zend_Form($config->user->login);
]]></programlisting>

        <para>
            ... и вся форма будет определена.
        </para>
    </sect2>

    <sect2 id="zend.form.quickstart.conclusion">
        <title>Заключение</title>

        <para>
            Надеемся, что благодаря этому небольшому обучающему руководству вы
            смогли получить представление о мощи и гибкости
            <code>Zend_Form</code>. Для получения более подробной
            информации читайте раздел далее.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
