mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-12 11:06:54 +01:00
360 lines
22 KiB
Plaintext
360 lines
22 KiB
Plaintext
Створення моделі
|
||
================
|
||
|
||
Перш, ніж писати HTML код для форми, нам потрібно визначити, які дані ми
|
||
будемо отримувати від користувачів і яким правилам вони повинні відповідати.
|
||
Для фіксації цієї інформації можна використовувати клас моделі даних.
|
||
Модель даних, як говориться у роздiлi [Модель](/doc/guide/basics.model),
|
||
це центральне місце для зберігання та перевірки даних, які вводить користувач.
|
||
|
||
Залежно від того, яким чином використовуються дані вводу, ми можемо створити два типи моделі даних.
|
||
Якщо ми отримуємо дані, оброблюємо їх, а потім видаляємо, то використовуємо [модель форми](/doc/guide/basics.model);
|
||
якщо ж отримуємо дані та зберігаємо їх у базу даних, використовуємо [Active Record](/doc/guide/database.ar).
|
||
Обидва типи моделей даних використовують один і той же базовий клас [CModel], який визначає
|
||
загальний інтерфейс, використовуваний формою.
|
||
|
||
> Note|Примітка: У прикладах цього розділу використовуються моделі форми. Тим не менш,
|
||
все може бути в рівній мірі застосовано і до моделей [Active Record](/doc/guide/database.ar).
|
||
|
||
Визначення класу моделі
|
||
-----------------------
|
||
|
||
Нижче ми створимо клас моделі `LoginForm`, який буде використовуватися для
|
||
отримання даних, що вводяться користувачем на сторінці аутентифікації. Оскільки
|
||
ці дані використовуються виключно з метою аутентифікації користувача та
|
||
зберігати їх не потрібно, то створимо модель `LoginForm` як модель форми.
|
||
|
||
~~~
|
||
[php]
|
||
class LoginForm extends CFormModel
|
||
{
|
||
public $username;
|
||
public $password;
|
||
public $rememberMe=false;
|
||
}
|
||
~~~
|
||
|
||
Ми оголосили у `LoginForm` три атрибути: `$username`, `$password` и `$rememberMe`.
|
||
Вони використовуються для зберігання імені користувача, пароля, а також значення опції зберігання
|
||
імені користувача. Так як `$rememberMe` за замовчуванням має значення `false`, то спочатку
|
||
у формі аутентифікації позначка цієї опції буде знята.
|
||
|
||
> Info|Інформація: Термін «атрибут» використовується для того, щоб відокремити властивості,
|
||
які визначаються цими змінними-членами класу, від інших властивостей.
|
||
Тут атрибут — це властивість, яка використовується в основному для зберігання даних,
|
||
які вводить користувач, або даних, які одержуємо із бази даних.
|
||
|
||
Визначення правил перевірки
|
||
---------------------------
|
||
|
||
У момент, коли користувач відправляє дані форми, а модель їх отримує,
|
||
нам необхідно переконатися, що ці дані коректні, перш, ніж ми будемо їх використовувати.
|
||
Це здійснюється за допомогою перевірки даних відповідно до набору правил.
|
||
Правила перевірки задаються в методі `rules()`, який повертає масив сконфігурованих правил.
|
||
|
||
~~~
|
||
[php]
|
||
class LoginForm extends CFormModel
|
||
{
|
||
public $username;
|
||
public $password;
|
||
public $rememberMe=false;
|
||
|
||
private $_identity;
|
||
|
||
public function rules()
|
||
{
|
||
return array(
|
||
array('username, password', 'required'),
|
||
array('rememberMe', 'boolean'),
|
||
array('password', 'authenticate'),
|
||
);
|
||
}
|
||
|
||
public function authenticate($attribute,$params)
|
||
{
|
||
$this->_identity=new UserIdentity($this->username,$this->password);
|
||
if(!$this->_identity->authenticate())
|
||
$this->addError('password','Невірне імʼя користувача або пароль.');
|
||
}
|
||
}
|
||
~~~
|
||
|
||
У наведеному вище коді, `username` та `password` — обовʼязкові для заповнення поля,
|
||
поле `password` повинне бути перевірене також на відповідність зазначеному імені користувача.
|
||
Поле `rememberMe` може приймати значення `true` або `false`.
|
||
|
||
Кожне правило, яке повертає `rules()`, повинно бути задане у наступному форматі:
|
||
|
||
~~~
|
||
[php]
|
||
array('AttributeList', 'Validator', 'on'=>'ScenarioList', …додаткові параметри)
|
||
~~~
|
||
|
||
де `AttributeList` — рядок з іменами атрибутів, відокремлених комами, які повинні
|
||
бути перевірені у відповідності з правилами; `Validator` вказує на тип перевірки, що використовується;
|
||
параметр `on` — необовʼязковий параметр, що встановлює список сценаріїв, де повинно
|
||
застосовуватися правило; а також інші параметри — пари імʼя-значення, які використовуються для
|
||
ініціалізації значень властивостей відповідного валідатора.
|
||
|
||
Починаючи із версії 1.1.11, сценарії можна вносити до "чорних" списків.
|
||
Якщо ви не бажаєте виконувати валідацію для деякого правила, коли активні конкрентні сценарії,
|
||
ви можете визначити параметр `except`, що містить їхні імена.
|
||
Синтакст такий же, як і для параметра `on`.
|
||
|
||
Перелік сценаріїв (параметри `on` та `except`) може бути визначений у двох різних формах,
|
||
які означають одне й те саме:
|
||
|
||
~~~
|
||
[php]
|
||
// довільний масив із іменами сценаріїв
|
||
'on'=>array('update', 'create'),
|
||
// рядок із іменами сценаріїв, розділених комами (пробіли ігноруються)
|
||
'except'=>'ignore, this, scenarios, at-all',
|
||
~~~
|
||
|
||
Є три способи вказати `Validator` у правилі перевірки. По-перше, `Validator` може бути іменем
|
||
метода у класі моделі даних, аналогічно `authenticate` у прикладі вище. Метод перевірки
|
||
оформлюється наступним чином:
|
||
|
||
~~~
|
||
[php]
|
||
/**
|
||
* @param string $attribute імʼя поля, яке будемо валідувати
|
||
* @param array $params додаткові параметри для правила валідації
|
||
*/
|
||
public function ValidatorName($attribute,$params) { … }
|
||
~~~
|
||
|
||
Другий спосіб — вказати `Validator` у якості імені класу. У цьому випадку для перевірки даних в момент
|
||
застосування правила створюється екземпляр класу перевірки. Додаткові параметри в правилі використовуються для
|
||
ініціалізації значень атрибутів екземпляра. Клас перевірки повинен бути похідним класом від [CValidator].
|
||
|
||
Третій варіант — визначити псевдонім класу валідатора. У прикладі вище, імʼя
|
||
`required` — це псевдонім класу [CRequiredValidator], який перевіряє, щоб значення атрибута, яке перевіряється,
|
||
не було порожнім. Нижче наведений повний перелік наперед визначених псевдонімів валідаторов,
|
||
які входять до складу Yii:
|
||
|
||
- `boolean`: псевдонім класу [CBooleanValidator], який перевіряє, щоб
|
||
атрибут мав значення або [CBooleanValidator::trueValue] або [CBooleanValidator::falseValue];
|
||
|
||
- `captcha`: псевдонім класу [CCaptchaValidator], який перевіряє, щоб
|
||
значення атрибуту було рівним коду верифікації на [капчі](http://ru.wikipedia.org/wiki/Captcha);
|
||
|
||
- `compare`: псевдонім класу [CCompareValidator], який перевіряє, щоб
|
||
значення атрибуту співпадало із значенням іншого атрибуту або константи;
|
||
|
||
- `email`: псевдонім класу [CEmailValidator], який відповідає за перевірку коректності email адреси;
|
||
|
||
- `date`: псевдонім класу [CDateValidator], який перевіряє, чи є атрибут коректної датою, часом або і тим і іншим.
|
||
|
||
- `default`: псевдонім класу [CDefaultValueValidator], який присвоює значення
|
||
за замовчуванням обраним атрибутам;
|
||
|
||
- `exist`: псевдонім класу [CExistValidator], який перевіряє наявність значення атрибута в зазначеному стовпці таблиці;
|
||
|
||
- `file`: псевдонім класу [CFileValidator], який відповідає за перевірку атрибуту на
|
||
наявність в ньому імені завантаженого файлу;
|
||
|
||
- `filter`: псевдонім класу [CFilterValidator], перетворюють атрибут з використанням фільтра;
|
||
|
||
- `in`: псевдонім класу [CRangeValidator], який перевіряє, чи містяться дані у заданому наборі значень;
|
||
|
||
- `length`: псевдонім класу [CStringValidator], який перевіряє, чи відповідає довжина даних заданому інтервалу;
|
||
|
||
- `match`: псевдонім класу [CRegularExpressionValidator], який перевіряє дані на відповідність регулярному виразу;
|
||
|
||
- `numerical`: псевдонім класу [CNumberValidator], який перевіряє, чи є дані коректним числовим значенням;
|
||
|
||
- `required`: псевдонім класу [CRequiredValidator], який перевіряє, щоб значення атрибуту не було порожнім;
|
||
|
||
- `type`: псевдонім класу [CTypeValidator], який перевіряє відповідність атрибута заданому типу даних;
|
||
|
||
- `unique`: псевдонім класу [CUniqueValidator], який перевіряє, чи є дані унікальними в межах поля бази даних;
|
||
|
||
- `url`: псевдонім класу [CUrlValidator], який відповідає за перевірку коректності URL.
|
||
|
||
Нижче представлені кілька прикладів використання наперед визначених валідаторов:
|
||
|
||
~~~
|
||
[php]
|
||
// імʼя користувача — обовʼязкове поле форми
|
||
array('username', 'required'),
|
||
// довжина імені користувача повинна бути від 3 до 12 символів включно
|
||
array('username', 'length', 'min'=>3, 'max'=>12),
|
||
// у сценарії регістрації значення полів «password» та «password2» повинні бути рівні
|
||
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
|
||
// у сценарії аутентифікації поле `password` повинно бути перевірене на відповідність вказаного імені користувача
|
||
array('password', 'authenticate', 'on'=>'login'),
|
||
~~~
|
||
|
||
|
||
Безпечне присвоювання значень атрибутів
|
||
----------------------------------------
|
||
|
||
Після того, як буде створений екземпляр моделі даних, нам часто потрібно заповнити його даними,
|
||
які ввів користувач. Це можна проробити легко та невимушено, використовуючи масове присвоювання:
|
||
|
||
~~~
|
||
[php]
|
||
$model=new LoginForm;
|
||
if(isset($_POST['LoginForm']))
|
||
$model->attributes=$_POST['LoginForm'];
|
||
~~~
|
||
|
||
Останній вираз у прикладі як раз і є масовим присвоюванням, де значення кожної змінної у `$_POST['LoginForm']`
|
||
присвоюється відповідному атрибуту моделі. Це еквівалентно наступної операції:
|
||
|
||
~~~
|
||
[php]
|
||
foreach($_POST['LoginForm'] as $name=>$value)
|
||
{
|
||
if($name є безпечним атрибутом)
|
||
$model->$name=$value;
|
||
}
|
||
~~~
|
||
|
||
Дуже важливо визначити які атрибут є безпечними. Наприклад, якщо ми зробимо первинний ключ таблицы безпечним,
|
||
зловмисник зможе отримати шанс його змінити та, таким чином, змінити дані, які він не повинен змінювати,
|
||
оскільки не авторизований для цього.
|
||
|
||
### Оголошення безпечних атрибутів
|
||
|
||
Атрибут вважається безпечним, якщо він зʼявляється в правилі валідації, вживаному в даному сценарії. Наприклад,
|
||
|
||
~~~
|
||
[php]
|
||
array('username, password', 'required', 'on'=>'login, register'),
|
||
array('email', 'required', 'on'=>'register'),
|
||
~~~
|
||
|
||
У коді вище атрибути `username` та `password` потрібні у сценарії `login`,
|
||
а атрибути `username`, `password` та `email` — у сценарії `register`. В результаті,
|
||
якщо ми виконуємо масове присвоювання у сценарії `login`, то тільки атрибути
|
||
`username` та `password` будуть масово присвоєні, так як тільки вони входять
|
||
в правило валідації для сценарія `login`. З іншої сторони, якщо поточним сценарієм є `register`,
|
||
то всі три атрибути можуть бути масово присвоєні.
|
||
|
||
~~~
|
||
[php]
|
||
// сценарій входу
|
||
$model=new User('login');
|
||
if(isset($_POST['User']))
|
||
$model->attributes=$_POST['User'];
|
||
|
||
// сценарій регістрації
|
||
$model=new User('register');
|
||
if(isset($_POST['User']))
|
||
$model->attributes=$_POST['User'];
|
||
~~~
|
||
|
||
Так чому ж ми використовуємо саме таку політику для визначення, чи є атрибут безпечним чи ні?
|
||
Якщо атрибут вже є в одному або декількох правилах валідації, навіщо турбуватися про щось ще?
|
||
|
||
Важливо памʼятати, що правила валідації використовуються для перевірки введених користувачем даних,
|
||
а не даних, які ми генеруємо в коді (наприклад, поточний час або автоматично згенерований первинний ключ).
|
||
Тому, НЕ ДОДАВАЙТЕ правила валідації для атрибутів, які не є введеними кінцевим користувачем.
|
||
|
||
Іноді ми хочемо оголосити атрибут безпечним навіть якщо в дійсності не маємо правила для нього.
|
||
Приклад - атрибут змісту статті, який може приймати будь-які дані, введені користувачем.
|
||
Для цього ми можемо використовувати спеціальне правило `safe`:
|
||
|
||
~~~
|
||
[php]
|
||
array('content', 'safe')
|
||
~~~
|
||
|
||
Для повноти картини, існує також правило `unsafe`, яке використовується для явної вказівки небезпечного атрибута:
|
||
|
||
~~~
|
||
[php]
|
||
array('permission', 'unsafe')
|
||
~~~
|
||
|
||
Правило `unsafe` використовується рідко і є протилежністю описаного нами раніше визначенням безпечних атрибутів.
|
||
|
||
У разі небезпечних вхідних даних, ми повинні привласнювати значення відповідним атрибутам,
|
||
використовуючи окремі операції привласнення, як представлено нижче:
|
||
|
||
~~~
|
||
[php]
|
||
$model->permission='admin';
|
||
$model->id=1;
|
||
~~~
|
||
|
||
|
||
Виконання перевірки
|
||
-------------------
|
||
|
||
Як тільки модель заповнена даними користувача, викликаємо метод [CModel::validate()],
|
||
щоб запустити процес перевірки. За підсумками перевірки метод повертає позитивний або негативний результат.
|
||
Для моделей [CActiveRecord] перевірку можна виконувати автоматично в момент виклику методу [CActiveRecord::save()].
|
||
|
||
Ми можемо прозначити сценарій через властивість [scenario|CModel::scenario] і, з його допомогою,
|
||
задати, який набір правил використовувати для перевірки.
|
||
|
||
Перевірка виконується в залежності від сценарію. Властивість [scenario|CModel::scenario] задає сценарій,
|
||
у якому застосовується модель і визначає який набір правил валідації використовувати.
|
||
Приміром, у сценарії `login`, ми хочемо перевірити лише поля моделі користувача `username` і `password`.
|
||
У сценарії `register` нам необхідно перевіряти більшу кількість даних: `email`, `address` і інших. Нижче показано, як провести перевірку для сценарію `register`:
|
||
|
||
~~~
|
||
[php]
|
||
// створюємо модель User і задаємо її сценарій як `register`. Вираз нижче еквівалентний наступному:
|
||
// $model=new User;
|
||
// $model->scenario='register';
|
||
$model=new User('register');
|
||
|
||
// наповнюємо модель даними
|
||
$model->attributes=$_POST['User'];
|
||
|
||
// проводимо валідацію
|
||
if($model->validate()) // якщо дані вірні
|
||
…
|
||
else
|
||
…
|
||
~~~
|
||
|
||
Сценарій для правил перевірки задається у властивості `on` правила.
|
||
Якщо `on` не визначено, правило використовується для всіх сценаріїв. Наприклад,
|
||
|
||
~~~
|
||
[php]
|
||
public function rules()
|
||
{
|
||
return array(
|
||
array('username, password', 'required'),
|
||
array('password_repeat', 'required', 'on'=>'register'),
|
||
array('password', 'compare', 'on'=>'register'),
|
||
);
|
||
}
|
||
~~~
|
||
|
||
Перше правило буде поширюватися на будь-які сценарії,
|
||
а два наступних будуть застосовуватися тільки до сценарію `register`.
|
||
|
||
Інформація про помилки
|
||
----------------------
|
||
|
||
Після перевірки всі можливі помилки знаходяться в обʼєкті моделі. Ми можемо отримати їх через
|
||
[CModel::getErrors()] та [CModel::getError()]. Перший метод повертає всі помилки для зазначеного атрибута моделі,
|
||
другий - тільки першу помилку.
|
||
|
||
Щоб дізнатися, чи виникли під час виконання перевірки будь-які помилки, можна скористатися методом
|
||
[CModel::hasErrors()]. І якщо помилки дійсно є, то отримати їх можна за допомогою методу
|
||
[CModel::getErrors()]. Обидва методи можуть бути використані як для всіх, так і для конкретного атрибуту.
|
||
|
||
Мітки атрибутів
|
||
---------------
|
||
|
||
Часто при роботі з формами для кожного поля потрібно відображати його позначку.
|
||
Вона підказує користувачеві, які дані йому потрібно ввести в поле.
|
||
Ми, звичайно, можемо задати мітки полів у представленні, але, якщо вказати їх безпосередньо в моделі даних,
|
||
ми виграємо в зручності і гнучкості.
|
||
|
||
За замовчуванням [CModel] у якості міток повертає назви атрибутів.
|
||
Змінити їх можна, якщо перевизначити метод [attributeLabels()|CModel::attributeLabels].
|
||
|
||
Далі ми побачимо, що можливість вказівки міток у моделі даних дозволяє швидко створювати складні форми.
|
||
|
||
<div class="revision">$Id: form.model.txt 3482 2011-12-13 09:41:36Z mdomba $</div> |