mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-12 11:06:54 +01:00
375 lines
23 KiB
Plaintext
375 lines
23 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` — необязательный параметр, определяющий список сценариев, в которых должно
|
||
использоваться правило; дополнительные параметры — это пары имя-значение, которые используются для
|
||
инициализации значений свойств соответствующих валидаторов.
|
||
|
||
Начиная с версии 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> |