Большая часть классов и пакетов, входящих в состав , может быть использована отдельно от фреймворка в целом, и набор классов не исключение. Это самостоятельный компонент, отлично поддающийся настройке. Но расплачиваться за это придется тем, что очевидные и простые вещи на первый взгляд придется реализовывать через ж довольно нетривиально с подключением (Decorators) и (View Helpers). Сложность разработки усугубляется еще и тем, что для одной задачи конечно же найдется несколько различных способов ее решения, порой сильно друг от друга отличающихся.
Вообще формы на вебе без преувеличения являются основным посредником «общения» между пользователем и приложением. Получается так, что форма соединяет в себе несколько частей приложения: клиентский код html и таблицы стилей, клиентские скрипты на javascript, обрабатывающие введенные пользователем данные на ранней стадии, и собственно саму обработку данных на стороне сервера. В серверную обработку входит фильтрация, валидация данных и их сохранение. В статье описаны следующие приемы:
- использование Zend_Form вне модели модель-вид-контроллер MVC
- отделение представления формы и ее рендеринга от структуры
- использование собственных валидаторов и декораторов
Сама форма, используемая в статье, работает . Исходники пока что можно скачать .
Zend_Form в первом приближении
Различные части компонента связаны между собой:
- Zend_Form состоит из элементов, унаследованных от Zend_Form_Element
- К элементам могут быть добавлены валидаторы и фильтры
- И сама форма и ее элементы зависят от декораторов, без которых не произвести рендеринг итогового html-кода. Декораторы организованы в цепочки, по которым каждый последующий декоратор добавляет к выводимому предыдущим коду свой html-код
- Стандартные декораторы класса передают рендеринг итогового html-кода помощникам вида
Использование 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 (которые должны заканчиваться на />).
Разделение структуры и представления
Создадим форму для интерфейса администратора, в котором можно отредактировать информацию о пользователе. Предположим, что согласно , некоторые админы могут редактировать профили пользователей, другие же только просматривать. Структура формы сведена к минимуму, а за конечное отображение отвечает в основном представление (вид).
Следуя правилам хорошего тона замусоривать контроллеры кодом, отвечающим за создание формы не нужно. Лучше всего унаследовать свой собственный класс формы от 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_Abstract, или же на основе Zend_Validator_Interface. При добавлении email или логинов на сайте необходимо удостовериться, что они еще не использовались в базе данных. Несмотря на то, что для подобных случаев в ZF уже есть необходимые валидаторы , мы в качестве примера напишем свой валидатор. Опустим часть кода, отвечающую за соединение с базой данных и выборку.
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');
}
Как я уже говорил, в оригинале автор статьи подключает еще и свой декоратор для ошибок. Мне его метод не понравился, поэтому опустим.
Рендеринг в режиме просмотра
По сути это упрощенная версия представления для редактирования. Основные отличия:
- Нет декоратора Form, значит и тэг form нигде не появляется
- Один общий декоратор для всех элементов My_Decorator_View_Element
- Поскольку этот единственный декоратор не использует Zend_View, его и не нужно подключать
My_Decorator_View_Element
Получает от элемента его значение и лейбл (надпись). После этого генерирует html-код, за исключением кнопки отправки (тупо игнорится) и массивов из чекбоксов (с множественным выбором).
Заключение
Примечательно, что Zend_Form вполне можно использовать в самописных приложениях, как и любые другие компоненты фреймворка. К сожалению, из-за «разношерстного» подхода к реализации частей любой системы стройность ее теряется.
Показанные классы для отображения формы можно использовать с экземплярами других классов, в чем конечно же большой плюс подхода. Без стандартных декораторов и валидаторов класс Zend_Form можно с легкостью подключать к собственным приложениям, не теряя его работоспособности.




