Files
yii/docs/guide/ru/form.model.txt
2011-01-16 14:55:27 +00:00

419 lines
24 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 код для формы, нам нужно определить, какие данные мы
будем получать от пользователей и каким правилам они должны соответствовать.
Для фиксации этой информации можно использовать класс модели данных.
Модель данных, как говорится в разделе [Модель](/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` — необязательный параметр, устанавливающий список сценариев, где должно
использоваться правило; а также прочие параметры — пары имя-значение, которые используются для
инициализации значений свойств соответствующего валидатора.
Есть три способа указать `Validator` в правиле проверки. Во-первых, `Validator` может быть именем
метода в классе модели данных, аналогично `authenticate` в примере выше. Метод проверки
оформляется следующим образом:
~~~
[php]
/**
* @param string имя поля, которое будем валидировать
* @param array дополнительные параметры для правила валидации
*/
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 адреса;
- `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;
}
~~~
Очень важно определить, какие атрибуты являются безопасными. Например, если мы
сделаем первичный ключ таблицы безопасным, злоумышленник сможет
получить шанс его изменить и, таким образом, изменить
данные, которые он не должен менять, поскольку не авторизован для этого.
Политика выбора безопасных атрибутов отличается в версиях 1.0
и 1.1. В дальнейшем мы расскажем о них по отдельности.
###Безопасные атрибуты в версии 1.1
В версии 1.1 атрибут считается безопасным, если он появляется в правиле
валидации, применяемом в данном сценарии. Например,
~~~
[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` используется редко и является противоположностью описанному
нами ранее определению безопасных атрибутов.
###Безопасные атрибуты в версии 1.0
В версии 1.0 безопасность входных данных задается при помощи метода `safeAttributes` и
конкретного сценария.
По умолчанию все public свойства [CFormModel] считаются безопасными.
Для [CActiveRecord] безопасными считаются все поля таблицы, кроме первичного ключа.
Мы можем переопределить `safeAttributes`, чтобы ограничить безопасные атрибуты
в соответствии с требованиями сценария. Например, модель, описывающая пользователя,
может содержать множество различных атрибутов, но в конкретном сценарии авторизации
нам требуются только атрибуты `username` и `password`:
~~~
[php]
public function safeAttributes()
{
return array(
parent::safeAttributes(),
'login' => 'username, password',
);
}
~~~
Будет аккуратнее, если структуру значения, возвращаемого методом
`safeAttributes` оформить так:
~~~
[php]
array(
// эти атрибуты могут быть *массово присвоены* в любом сценарии,
// кроме специально обозначенных ниже
'attr1, attr2, …',
*
// эти атрибуты можно *присвоить массово* только в сценарии 'scenario1'
'scenario1' => 'attr2, attr3, …',
*
// а эти — только в сценарии 'scenario2'
'scenario2' => 'attr1, attr3, …',
)
~~~
Если модель не завязана на сценарии (т.е. используется только в одном сценарии или же
все сценарии работают с единым набором безопасных атрибутов), тогда возвращаемое
значение можно упростить до одной строки:
~~~
[php]
'attr1, attr2, …'
~~~
В случае небезопасных входных данных, мы должны присваивать значения
соответствующим атрибутам, используя отдельные операции присваивания, как представлено ниже:
~~~
[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 2285 2010-07-28 20:40:00Z qiang.xue $</div>