mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-11 02:26:56 +01:00
416 lines
22 KiB
Plaintext
416 lines
22 KiB
Plaintext
Використання конструктора форм
|
||
==============================
|
||
|
||
При створенні HTML форм часто доводиться писати досить велику кількість повторюваного коду, який майже неможливо використовувати в інших проектах. Приміром, для кожного поля вводу нам необхідно вивести опис і можливі помилки валідації. Для того, щоб зробити можливим повторне використання подібного коду, можна використовувати конструктор форм.
|
||
|
||
|
||
Загальна ідея
|
||
-------------
|
||
|
||
Конструктор форм використовує обʼєкт [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.
|
||
|
||
Слід окремо зупинитися на властивості [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">
|
||
<?php $this->renderPartial('_form', array('form'=>$form)); ?>
|
||
</div>
|
||
~~~
|
||
|
||
Якщо стандартний рендеринг форми не підходить (наприклад, у формі потрібні унікальні декоративні елементи для певних полів), у відображенні можна вчинити наступним чином:
|
||
|
||
~~~
|
||
[php]
|
||
які-небудь складні елементи інтерфейса
|
||
|
||
<?php echo $form['username']; ?>
|
||
|
||
які-небудь складні елементи інтерфейса
|
||
|
||
<?php echo $form['password']; ?>
|
||
|
||
які-небудь складні елементи інтерфейса
|
||
~~~
|
||
|
||
У цьому випадку конструктор форм не дуже ефективний, оскільки нам доводиться описувати ті ж обсяги коду форми. Проте, перевага є. Вона в тому, що форма, описана в окремому файлі конфігурації, дозволяє розробнику сфокусуватися на логіці.
|