mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-06 16:16:53 +01:00
419 lines
24 KiB
Plaintext
419 lines
24 KiB
Plaintext
Создание модели
|
||
===============
|
||
|
||
Прежде, чем писать 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> |