Files
yii/docs/guide/uk/form.builder.txt
2013-02-20 14:44:01 +02:00

416 lines
22 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Використання конструктора форм
==============================
При створенні 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']; ?>
які-небудь складні елементи інтерфейса
~~~
У цьому випадку конструктор форм не дуже ефективний, оскільки нам доводиться описувати ті ж обсяги коду форми. Проте, перевага є. Вона в тому, що форма, описана в окремому файлі конфігурації, дозволяє розробнику сфокусуватися на логіці.