mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-06 00:04:07 +01:00
508 lines
23 KiB
Plaintext
508 lines
23 KiB
Plaintext
Использование построителя форм
|
||
==============================
|
||
|
||
При создании HTML форм часто приходится писать довольно большое количество повторяющегося кода,
|
||
который почти невозможно использовать в других проектах. К примеру, для каждого поля ввода
|
||
нам необходимо вывести описание и возможные ошибки валидации. Для того, чтобы сделать
|
||
возможным повторное использование подобного кода, можно использовать построитель форм, доступный
|
||
с версии Yii 1.1.0.
|
||
|
||
|
||
Общая идея
|
||
----------
|
||
|
||
Построитель форм использует объект [CForm] для описания параметров, необходимых для
|
||
создания HTML формы, таких как модели и поля, используемые в форме, а также параметры
|
||
построения самой формы. Разработчику достаточно создать объект [CForm], задать его параметры
|
||
и вызвать его метод для построения формы.
|
||
|
||
Параметры формы организованы в виде иерархии элементов формы. Корнем является
|
||
объект [CForm]. Корневой объект формы включает в себя две коллекции, содержащие
|
||
другие элементы: [CForm::buttons] и [CForm::elements]. Первая содержит кнопки
|
||
(такие как «Сохранить» или «Очистить»), вторая — поля ввода, статический текст и
|
||
вложенные формы — объекты [CForm], находящиеся в коллекции [CForm::elements]
|
||
другой формы. Вложенная форма может иметь свою модель данных и коллекции
|
||
[CForm::buttons] и [CForm::elements].
|
||
|
||
Когда пользователи отправляют форму, данные, введённые в поля ввода всей иерархии формы, включая
|
||
вложенные формы, передаются на сервер. [CForm] включает в себя методы, позволяющие
|
||
автоматически присвоить данные полям соответствующей модели и провести валидацию.
|
||
|
||
|
||
Создание простой формы
|
||
----------------------
|
||
|
||
Ниже будет показано, как построить форму входа на сайт.
|
||
|
||
Сначала реализуем действие `login`:
|
||
|
||
~~~
|
||
[php]
|
||
public function actionLogin()
|
||
{
|
||
$model = new LoginForm;
|
||
$form = new CForm('application.views.site.loginForm', $model);
|
||
if($form->submitted('login') && $form->validate())
|
||
$this->redirect(array('site/index'));
|
||
else
|
||
$this->render('login', array('form'=>$form));
|
||
}
|
||
~~~
|
||
|
||
Вкратце, здесь мы создали объект [CForm], используя конфигурацию, найденную
|
||
по пути, который задан псевдонимом `application.views.site.loginForm`.
|
||
Объект [CForm], как описано в разделе «[Создание модели](/doc/guide/form.model)»,
|
||
использует модель `LoginForm`.
|
||
|
||
Если форма отправлена и все входные данные прошли проверку без ошибок,
|
||
перенаправляем пользователя на страницу `site/index`. Иначе, выводим представление
|
||
`login`, описывающее форму.
|
||
|
||
Псевдоним пути `application.views.site.loginForm` указывает на файл PHP
|
||
`protected/views/site/loginForm.php`. Этот файл возвращает массив, описывающий
|
||
настройки, необходимые для [CForm]:
|
||
|
||
~~~
|
||
[php]
|
||
return array(
|
||
'title'=>'Пожалуйста, представьтесь',
|
||
|
||
'elements'=>array(
|
||
'username'=>array(
|
||
'type'=>'text',
|
||
'maxlength'=>32,
|
||
),
|
||
'password'=>array(
|
||
'type'=>'password',
|
||
'maxlength'=>32,
|
||
),
|
||
'rememberMe'=>array(
|
||
'type'=>'checkbox',
|
||
)
|
||
),
|
||
|
||
'buttons'=>array(
|
||
'login'=>array(
|
||
'type'=>'submit',
|
||
'label'=>'Вход',
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
Настройки, приведённые выше являются ассоциативным массивом, состоящем из пар имя-значение,
|
||
используемых для инициализации соответствующих свойств [CForm]. Самыми важными
|
||
свойствами, как мы уже упомянули, являются [CForm::elements] и [CForm::buttons].
|
||
Каждое из них содержит массив, определяющий элементы формы. Более детальное описание
|
||
элементов формы будет приведено в следующем подразделе.
|
||
|
||
Опишем шаблон представления `login`:
|
||
|
||
~~~
|
||
[php]
|
||
<h1>Вход</h1>
|
||
|
||
<div class="form">
|
||
<?php echo $form; ?>
|
||
</div>
|
||
~~~
|
||
|
||
> Tip|Подсказка: Приведённый выше код `echo $form;` эквивалентен `echo $form->render();`.
|
||
> Использование более компактной записи возможно так как [CForm] реализует магический
|
||
> метод `__toString`, в котором вызывается метод `render()`, возвращающий
|
||
> код формы.
|
||
|
||
|
||
Описание элементов формы
|
||
------------------------
|
||
|
||
При использовании построителя форм, вместо написания разметки мы, главным образом,
|
||
описываем элементы формы. В данном подразделе мы опишем, как задать свойство [CForm::elements].
|
||
Мы не будем описывать [CForm::buttons] так как конфигурация этого свойства практически
|
||
ничем не отличается от [CForm::elements].
|
||
|
||
Свойство [CForm::elements] является массивом, каждый элемент которого соответствует
|
||
элементу формы. Это может быть поле ввода, статический текст или вложенная форма.
|
||
|
||
### Описание поля ввода
|
||
|
||
Поле ввода, главным образом, состоит из заголовка, самого поля, подсказки и текста ошибки и
|
||
должно соответствовать определённому атрибуту модели. Описание поля ввода содержится в
|
||
экземпляре класса [CFormInputElement]. Приведённый ниже код массива [CForm::elements]
|
||
описывает одно поле ввода:
|
||
|
||
~~~
|
||
[php]
|
||
'username'=>array(
|
||
'type'=>'text',
|
||
'maxlength'=>32,
|
||
),
|
||
~~~
|
||
|
||
Здесь указано, что атрибут модели называется `username`, тип поля — `text` и его атрибут
|
||
`maxlength` равен 32.
|
||
|
||
Любое доступное для записи свойство [CFormInputElement] может быть настроено показанным выше
|
||
способом. К примеру, можно задать свойство [hint|CFormInputElement::hint] для того, чтобы
|
||
показывать подсказку или свойство [items|CFormInputElement::items], если
|
||
поле является выпадающим списком или группой элементов checkbox или radio.
|
||
Если имя опции не является свойством [CFormInputElement], оно будет считаться атрибутом
|
||
соответствующего HTML-тега input. К примеру, так как выше опция `maxlength` не является
|
||
свойством [CFormInputElement], она будет использована как атрибут `maxlength` HTML-элемента
|
||
input.
|
||
|
||
~~~
|
||
Мы можем задать и другие доступные для записи свойства [CFormInputElement].
|
||
|
||
~~~
|
||
|
||
Следует отдельно остановиться на свойстве [type|CFormInputElement::type].
|
||
Оно определяет тип поля ввода. К примеру, тип `text` означает, что будет использован
|
||
элемент формы `input`, а `password` — поле для ввода пароля. В [CFormInputElement]
|
||
реализованы следующие типы полей ввода:
|
||
|
||
- text
|
||
- hidden
|
||
- password
|
||
- textarea
|
||
- file
|
||
- radio
|
||
- checkbox
|
||
- listbox
|
||
- dropdownlist
|
||
- checkboxlist
|
||
- radiolist
|
||
|
||
Отдельно следует описать использование "списочных" типов `dropdownlist`, `checkboxlist`
|
||
и `radiolist`. Для них необходимо задать свойство [items|CFormInputElement::items]
|
||
соответствующего элемента input. Сделать это можно так:
|
||
|
||
~~~
|
||
[php]
|
||
'gender'=>array(
|
||
'type'=>'dropdownlist',
|
||
'items'=>User::model()->getGenderOptions(),
|
||
'prompt'=>'Выберите значение:',
|
||
),
|
||
|
||
…
|
||
|
||
class User extends CActiveRecord
|
||
{
|
||
public function getGenderOptions()
|
||
{
|
||
return array(
|
||
0 => 'Мужчина',
|
||
1 => 'Женщина',
|
||
);
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Данный код сгенерирует выпадающий список с текстом «Выберите значение:» и опциями
|
||
«Мужчина» и «Женщина», которые мы получаем из метода `getGenderOptions` модели `User`.
|
||
|
||
Кроме данных типов полей, в свойстве [type|CFormInputElement::type] можно указать
|
||
класс или псевдоним пути виджета. Класс виджета должен наследовать [CInputWidget] или [CJuiInputWidget].
|
||
При генерации элемента формы, будет создан и выполнен экземпляр класса виджета.
|
||
Виджет будет использовать конфигурацию, переданную через настройки элемента формы.
|
||
|
||
|
||
### Описание статического текста
|
||
|
||
Довольно часто в форме, помимо полей ввода, содержится некоторая декоративная
|
||
HTML разметка. К примеру, горизонтальный разделитель для выделения определённых
|
||
частей формы или изображение, улучшающее внешний вид формы. Подобный HTML код
|
||
можно описать в коллекции [CForm::elements] как статический текст. Для этого
|
||
в [CForm::elements] в нужном нам месте вместо массива необходимо использовать строку.
|
||
К примеру:
|
||
|
||
~~~
|
||
[php]
|
||
return array(
|
||
'elements'=>array(
|
||
......
|
||
'password'=>array(
|
||
'type'=>'password',
|
||
'maxlength'=>32,
|
||
),
|
||
|
||
'<hr />',
|
||
|
||
'rememberMe'=>array(
|
||
'type'=>'checkbox',
|
||
)
|
||
),
|
||
......
|
||
);
|
||
~~~
|
||
|
||
В приведённом коде мы вставили горизонтальный разделитель между полями `password` и `rememberMe`.
|
||
|
||
Статический текст лучше всего использовать в том случае, когда разметка и её расположение
|
||
достаточно уникальны. Если некоторую разметку должен содержать каждый элемент формы,
|
||
лучше всего переопределить непосредственно построение разметки формы, как будет описано далее.
|
||
|
||
|
||
### Описание вложенных форм
|
||
|
||
Вложенные формы используются для разделения сложных форм на несколько связанных простых. К примеру,
|
||
мы можем разделить форму регистрации пользователя на две вложенные формы: данные для входа и
|
||
данные профиля. Каждая вложенная форма может, хотя и не обязана, быть связана с моделью данных.
|
||
В примере с формой регистрации, если мы храним данные для входа и данные профиля в
|
||
двух разных таблицах (и соответственно в двух моделях), то каждая вложенная форма
|
||
будет сопоставлена соответствующей модели данных. Если же все данные хранятся в
|
||
одной таблице, ни одна из вложенных форм не будет привязана к модели и обе будут
|
||
использовать модель главной формы.
|
||
|
||
Вложенная форма, как и главная, описывается объектом [CForm]. Для того, чтобы
|
||
описать вложенную форму, необходимо определить элемент типа `form` в
|
||
свойстве [CForm::elements]:
|
||
|
||
~~~
|
||
[php]
|
||
return array(
|
||
'elements'=>array(
|
||
......
|
||
'user'=>array(
|
||
'type'=>'form',
|
||
'title'=>'Данные для входа',
|
||
'elements'=>array(
|
||
'username'=>array(
|
||
'type'=>'text',
|
||
),
|
||
'password'=>array(
|
||
'type'=>'password',
|
||
),
|
||
'email'=>array(
|
||
'type'=>'text',
|
||
),
|
||
),
|
||
),
|
||
|
||
'profile'=>array(
|
||
'type'=>'form',
|
||
......
|
||
),
|
||
......
|
||
),
|
||
......
|
||
);
|
||
~~~
|
||
|
||
Также, как и у главной формы, у вложенной формы необходимо задать свойство [CForm::elements].
|
||
Если вложенной форме необходимо сопоставить модель данных, можно сделать это задав свойство [CForm::model].
|
||
|
||
В некоторых случаях бывает полезно определить форму в объекте класса, отличного от [CForm].
|
||
К примеру, как будет показано ниже, можно расширить [CForm] для реализации своего
|
||
алгоритма построения разметки. При указании типа элемента `form`, вложенная форма
|
||
будет автоматически использовать объект того же класса, что и у главной формы.
|
||
Если указать тип элемента как, например, `XyzForm` (строка, оканчивающаяся на `Form`),
|
||
то вложенная форма будет использовать объект класса `XyzForm`.
|
||
|
||
|
||
Доступ к элементам формы
|
||
------------------------
|
||
|
||
Обращаться к элементам формы так же просто, как и к элементам массива. Свойство [CForm::elements] возвращает
|
||
объект [CFormElementCollection], наследуемый от [CMap] и позволяет получить доступ к своим элементам как к
|
||
элементам массива. К примеру, для того, чтобы обратиться к элементу `username` формы `login` из примера,
|
||
можно использовать следующий код:
|
||
|
||
~~~
|
||
[php]
|
||
$username = $form->elements['username'];
|
||
~~~
|
||
|
||
Для доступа к элементу `email` формы регистрации пользователя из примера,
|
||
можно использовать:
|
||
~~~
|
||
[php]
|
||
$email = $form->elements['user']->elements['email'];
|
||
~~~
|
||
|
||
Так как [CForm] реализует доступ к элементам [CForm::elements] как к массиву, можно упростить
|
||
код до:
|
||
|
||
~~~
|
||
[php]
|
||
$username = $form['username'];
|
||
$email = $form['user']['email'];
|
||
~~~
|
||
|
||
|
||
Создание вложенной формы
|
||
------------------------
|
||
|
||
Ранее мы уже описывали вложенные формы. Форма в которой имеются вложенные формы называется главной.
|
||
В данном разделе мы будем использовать форму регистрации пользователя в качестве примера
|
||
создания вложенных форм, соответствующих нескольким моделям данных. Далее данные для входа
|
||
пользователя хранятся в модели `User`, а данные профиля — в модели `Profile`.
|
||
|
||
Реализуем действие `register` следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
public function actionRegister()
|
||
{
|
||
$form = new CForm('application.views.user.registerForm');
|
||
$form['user']->model = new User;
|
||
$form['profile']->model = new Profile;
|
||
if($form->submitted('register') && $form->validate())
|
||
{
|
||
$user = $form['user']->model;
|
||
$profile = $form['profile']->model;
|
||
if($user->save(false))
|
||
{
|
||
$profile->userID = $user->id;
|
||
$profile->save(false);
|
||
$this->redirect(array('site/index'));
|
||
}
|
||
}
|
||
|
||
$this->render('register', array('form'=>$form));
|
||
}
|
||
~~~
|
||
|
||
Выше мы создаём форму, используя настройки из `application.views.user.registerForm`.
|
||
После отправки данных формы и их успешной валидации, мы пытаемся сохранить модели
|
||
пользовательских данных и профиля. Мы получаем модели через свойство `model` соответствующего
|
||
объекта вложенной формы. Так как валидация уже пройдена, вызываем `$user->save(false)`
|
||
с параметром `false`, позволяющим её не проводить повторно. Точно так же поступаем с
|
||
моделью профиля.
|
||
|
||
Далее описываем настройки формы в файле `protected/views/user/registerForm.php`:
|
||
|
||
~~~
|
||
[php]
|
||
return array(
|
||
'elements'=>array(
|
||
'user'=>array(
|
||
'type'=>'form',
|
||
'title'=>'Данные для входа',
|
||
'elements'=>array(
|
||
'username'=>array(
|
||
'type'=>'text',
|
||
),
|
||
'password'=>array(
|
||
'type'=>'password',
|
||
),
|
||
'email'=>array(
|
||
'type'=>'text',
|
||
)
|
||
),
|
||
),
|
||
|
||
'profile'=>array(
|
||
'type'=>'form',
|
||
'title'=>'Профиль',
|
||
'elements'=>array(
|
||
'firstName'=>array(
|
||
'type'=>'text',
|
||
),
|
||
'lastName'=>array(
|
||
'type'=>'text',
|
||
),
|
||
),
|
||
),
|
||
),
|
||
|
||
'buttons'=>array(
|
||
'register'=>array(
|
||
'type'=>'submit',
|
||
'label'=>'Зарегистрироваться',
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
При задании каждой вложенной формы, мы указываем свойство [CForm::title].
|
||
По умолчанию, при построении HTML-формы каждая вложенная форма будет выведена в
|
||
`fieldset` с заданным нами заголовком.
|
||
|
||
Описываем очень простой код шаблона представления `register`:
|
||
|
||
~~~
|
||
[php]
|
||
<h1>Регистрация</h1>
|
||
|
||
<div class="form">
|
||
<?php echo $form; ?>
|
||
</div>
|
||
~~~
|
||
|
||
|
||
Свой рендеринг формы
|
||
--------------------
|
||
|
||
Главное преимущество при использовании построителя форм — разделение логики
|
||
(конфигурация формы хранится в отдельном файле) и отображения (метод [CForm::render]).
|
||
В результате, мы можем настроить рендеринг формы либо переопределением метода
|
||
[CForm::render], либо своим отображением. Оба варианта позволяют не менять конфигурацию
|
||
и позволяют использовать её повторно.
|
||
|
||
При переопределении [CForm::render], необходимо, главным образом, обойти коллекции [CForm::elements]
|
||
и [CForm::buttons] и вызвать метод [CFormElement::render] для каждого элемента. К примеру:
|
||
|
||
~~~
|
||
[php]
|
||
class MyForm extends CForm
|
||
{
|
||
public function render()
|
||
{
|
||
$output = $this->renderBegin();
|
||
|
||
foreach($this->getElements() as $element)
|
||
$output .= $element->render();
|
||
|
||
$output .= $this->renderEnd();
|
||
|
||
return $output;
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Также можно использовать отображение `_form`:
|
||
|
||
~~~
|
||
[php]
|
||
<?php
|
||
echo $form->renderBegin();
|
||
|
||
foreach($form->getElements() as $element)
|
||
echo $element->render();
|
||
|
||
echo $form->renderEnd();
|
||
~~~
|
||
|
||
Для этого достаточно:
|
||
|
||
~~~
|
||
[php]
|
||
<div class="form">
|
||
$this->renderPartial('_form', array('form'=>$form));
|
||
</div>
|
||
~~~
|
||
|
||
Если стандартный рендеринг формы не подходит (к примеру, в форме
|
||
нужны уникальные декоративные элементы для определённых полей),
|
||
в отображении можно поступить следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
какие-нибудь сложные элементы интерфейса
|
||
|
||
<?php echo $form['username']; ?>
|
||
|
||
какие-нибудь сложные элементы интерфейса
|
||
|
||
<?php echo $form['password']; ?>
|
||
|
||
какие-нибудь сложные элементы интерфейса
|
||
~~~
|
||
|
||
В этом случае построитель форм не очень эффективен так как нам приходится описывать
|
||
те же объёмы кода формы. Тем не менее, преимущество есть. Оно в том, что форма,
|
||
описанная в отдельном файле конфигурации, позволяет разработчику сфокусироваться на логике.
|
||
|
||
<div class="revision">$Id: form.builder.txt 2353 2010-08-28 20:45:26Z qiang.xue $</div> |