При создании HTML форм часто приходится писать довольно большое количество повторяющегося кода, который почти невозможно использовать в других проектах. К примеру, для каждого поля ввода нам необходимо вывести описание и возможные ошибки валидации. Для того чтобы сделать возможным повторное использование подобного кода, можно использовать конструктор форм.
Конструктор форм использует объект CForm для описания параметров, необходимых для создания HTML формы, таких как модели и поля, используемые в форме, а также параметры построения самой формы. Разработчику достаточно создать объект CForm, задать его параметры и вызвать метод для построения формы.
Параметры формы организованы в виде иерархии элементов формы. Корнем является объект CForm. Корневой объект формы включает в себя две коллекции, содержащие другие элементы: CForm::buttons и CForm::elements. Первая содержит кнопки (такие как «Сохранить» или «Очистить»), вторая — поля ввода, статический текст и вложенные формы — объекты CForm, находящиеся в коллекции CForm::elements другой формы. Вложенная форма может иметь свою модель данных и коллекции CForm::buttons и CForm::elements.
Когда пользователи отправляют форму, данные, введённые в поля ввода всей иерархии формы, включая вложенные формы, передаются на сервер. CForm включает в себя методы, позволяющие автоматически присвоить данные полям соответствующей модели и провести валидацию.
Ниже будет показано, как построить форму входа на сайт.
Сначала реализуем действие login
:
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, как описано в разделе «Создание модели»,
использует модель LoginForm
.
Если форма отправлена, и все входные данные прошли проверку без ошибок,
перенаправляем пользователя на страницу site/index
. Иначе выводим представление
login
, описывающее форму.
Псевдоним пути application.views.site.loginForm
указывает на файл PHP
protected/views/site/loginForm.php
. Этот файл возвращает массив, описывающий
настройки, необходимые для CForm:
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
:
<h1>Вход</h1> <div class="form"> echo $form; </div>
Подсказка: Приведённый выше код
echo $form;
эквивалентенecho $form->render();
. Использование более компактной записи возможно, так как CForm реализует магический метод__toString
, в котором вызывается методrender()
, возвращающий код формы.
При использовании конструктора форм вместо написания разметки мы, главным образом, описываем элементы формы. В данном подразделе мы опишем, как задать свойство CForm::elements. Мы не будем описывать CForm::buttons, так как конфигурация этого свойства практически ничем не отличается от CForm::elements.
Свойство CForm::elements является массивом, каждый элемент которого соответствует элементу формы. Это может быть поле ввода, статический текст или вложенная форма.
Поле ввода, главным образом, состоит из заголовка, самого поля, подсказки и текста ошибки и должно соответствовать определённому атрибуту модели. Описание поля ввода содержится в экземпляре класса CFormInputElement. Приведённый ниже код массива CForm::elements описывает одно поле ввода:
'username'=>array(
'type'=>'text',
'maxlength'=>32,
),
Здесь указано, что атрибут модели называется username
, тип поля — text
и его атрибут
maxlength
равен 32.
Любое доступное для записи свойство CFormInputElement может быть настроено приведённым выше
способом. К примеру, можно задать свойство hint для того, чтобы
показывать подсказку или свойство items, если
поле является выпадающим списком или группой элементов checkbox или radio.
Если имя опции не является свойством CFormInputElement, оно будет считаться атрибутом
соответствующего HTML-тега input. Например, так как опция maxlength
не является
свойством CFormInputElement, она будет использована как атрибут maxlength
HTML-элемента
input.
Следует отдельно остановиться на свойстве type.
Оно определяет тип поля ввода. К примеру, тип text
означает, что будет использован
элемент формы input
, а password
— поле для ввода пароля. В CFormInputElement
реализованы следующие типы полей ввода:
Отдельно следует описать использование "списочных" типов dropdownlist
, checkboxlist
и radiolist
. Для них необходимо задать свойство items
соответствующего элемента input. Сделать это можно так:
'gender'=>array(
'type'=>'dropdownlist',
'items'=>User::model()->getGenderOptions(),
'prompt'=>'Выберите значение:',
),
…
class User extends CActiveRecord
{
public function getGenderOptions()
{
return array(
0 => 'Мужчина',
1 => 'Женщина',
);
}
}
Данный код сгенерирует выпадающий список с текстом «Выберите значение:» и опциями
«Мужчина» и «Женщина», которые мы получаем из метода getGenderOptions
модели User
.
Кроме данных типов полей, в свойстве type можно указать класс или псевдоним пути виджета. Класс виджета должен наследовать CInputWidget или CJuiInputWidget. В ходе генерации элемента формы будет создан и выполнен экземпляр класса виджета. Виджет будет использовать конфигурацию, переданную через настройки элемента формы.
Довольно часто в форме, помимо полей ввода, содержится некоторая декоративная HTML разметка. К примеру, горизонтальный разделитель для выделения определённых частей формы или изображение, улучшающее внешний вид формы. Подобный HTML код можно описать в коллекции CForm::elements как статический текст. Для этого в CForm::elements в нужном нам месте вместо массива необходимо использовать строку. Например:
return array(
'elements'=>array(
......
'password'=>array(
'type'=>'password',
'maxlength'=>32,
),
'<hr />',
'rememberMe'=>array(
'type'=>'checkbox',
)
),
......
);
В приведённом коде мы вставили горизонтальный разделитель между полями password
и rememberMe
.
Статический текст лучше всего использовать в том случае, когда разметка и её расположение достаточно уникальны. Если некоторую разметку должен содержать каждый элемент формы, лучше всего переопределить непосредственно построение разметки формы, как будет описано далее.
Вложенные формы используются для разделения сложных форм на несколько связанных простых. К примеру, мы можем разделить форму регистрации пользователя на две вложенные формы: данные для входа и данные профиля. Каждая вложенная форма может (хотя и не обязана) быть связана с моделью данных. В примере с формой регистрации, если мы храним данные для входа и данные профиля в двух разных таблицах (и, соответственно, в двух моделях), то каждая вложенная форма будет сопоставлена соответствующей модели данных. Если же все данные хранятся в одной таблице, ни одна из вложенных форм не будет привязана к модели и обе будут использовать модель главной формы.
Вложенная форма, как и главная, описывается объектом CForm. Для того чтобы
описать вложенную форму, необходимо определить элемент типа form
в
свойстве CForm::elements:
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
из вышеприведённого примера,
можно использовать следующий код:
$username = $form->elements['username'];
Аналогично, для доступа к элементу email
формы регистрации, можно использовать следующий код:
$email = $form->elements['user']->elements['email'];
Так как CForm реализует доступ к элементам CForm::elements как к массиву, можно упростить приведённый код до:
$username = $form['username'];
$email = $form['user']['email'];
Ранее мы уже описывали вложенные формы. Форма, содержащая вложенные формы, называется главной.
В данном разделе мы будем использовать форму регистрации пользователя в качестве примера
создания вложенных форм, соответствующих нескольким моделям данных. Далее данные для входа
пользователя хранятся в модели User
, а данные профиля — в модели Profile
.
Реализуем действие register
следующим образом:
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
:
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
:
<h1>Регистрация</h1> <div class="form"> echo $form; </div>
Главное преимущество при использовании конструктора форм — разделение логики (конфигурация формы хранится в отдельном файле) и отображения (метод CForm::render). Мы можем настроить рендеринг формы, переопределив метод CForm::render либо используя собственный файл представления. Оба варианта позволяют не менять конфигурацию формы и использовать её повторно.
При переопределении CForm::render необходимо, главным образом, обойти коллекции CForm::elements и CForm::buttons и вызвать метод CFormElement::render для каждого элемента. Например:
class MyForm extends CForm
{
public function render()
{
$output = $this->renderBegin();
foreach($this->getElements() as $element)
$output .= $element->render();
$output .= $this->renderEnd();
return $output;
}
}
Также можно использовать представление _form
:
echo $form->renderBegin(); foreach($form->getElements() as $element) echo $element->render(); echo $form->renderEnd();
Для этого достаточно написать:
<div class="form"> $this->renderPartial('_form', array('form'=>$form)); </div>
Если стандартный рендеринг формы не подходит (к примеру, в форме нужны уникальные декоративные элементы для определённых полей), в представлении можно поступить следующим образом:
какие-нибудь сложные элементы интерфейса echo $form['username']; какие-нибудь сложные элементы интерфейса echo $form['password']; какие-нибудь сложные элементы интерфейса
В этом случае конструктор форм не очень эффективен, так как нам приходится описывать те же объёмы кода формы. Тем не менее, преимущество есть. Оно в том, что форма, описанная в отдельном файле конфигурации, позволяет разработчику сфокусироваться на логике.
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.