0 follower

Создание модели

Прежде, чем писать HTML код для формы, нам нужно определить, какие данные мы будем получать от пользователей и каким правилам они должны соответствовать. Для фиксации этой информации можно использовать класс модели данных. Модель данных, как говорится в разделе Модель, это центральное место для хранения и проверки данных, вводимых пользователем.

В зависимости от того, каким образом используются данные ввода, мы можем создать два типа модели данных. Если мы получаем данные, обрабатываем их, а затем удаляем, то используем модель формы; если же получаем данные и сохраняем их в базу данных, используем Active Record. Оба типа моделей данных используют один и тот же базовый класс CModel, который определяет общий интерфейс используемый формой.

Примечание: В примерах этого раздела используются модели формы. Тем не менее, всё может быть в равной степени применено и к моделям Active Record.

1. Определение класса модели

Ниже мы создадим класс модели LoginForm, который будет использоваться для получения данных, вводимых пользователем на странице авторизации. Поскольку эти данные используются исключительно в целях аутентификации пользователя и сохранять их не требуется, то создадим модель LoginForm как модель формы.

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
}

Мы объявили в LoginForm три атрибута: $username, $password и $rememberMe. Они используются для хранения имени пользователя, пароля, а также значения опции сохранения имени пользователя. Так как $rememberMe по умолчанию имеет значение false, то изначально в форме авторизации галочка этой опции будет снята.

Информация: Термин "атрибут" используется, чтобы отделить свойства, определяемые этими переменными-членами класса, от прочих свойств. Здесь атрибут - это свойство, которое используется в основном для хранения данных, вводимых пользователем, или данных, получаемых из базы данных.

2. Определение правил проверки

В момент, когда пользователь отправляет данные формы, а модель их получает, нам необходимо удостовериться, что эти данные корректны, прежде, чем мы будем их использовать. Это осуществляется посредством проверки данных в соответствии с набором правил. Правила проверки задаются в методе rules(), который возвращает массив сконфигурированных правил.

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
 
    public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('password', 'authenticate'),
    );
    }
 
    public function authenticate($attribute,$params)
    {
        if(!$this->hasErrors())  // аутентификация производится только если нет ошибок ввода
        {
            $identity=new UserIdentity($this->username,$this->password);
            if($identity->authenticate())
            {
                $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days
                Yii::app()->user->login($identity,$duration);
            }
            else
                $this->addError('password','Incorrect password.');
        }
    }
}

В коде, представленном выше, username и password - обязательные для заполнения поля, а поле password должно быть проверено также на соответствие указанному имени пользователя.

Каждое правило, возвращаемое rules(), должно быть задано в следующем формате:

array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...дополнительные параметры)

где AttributeList - строка с именами атрибутов, отделенных запятыми, которые должны быть проверены в соответствии с правилами; Validator указывает на тип используемой проверки; параметр on - необязательный параметр, устанавливающий список сценариев, где должно использоваться правило; а также прочие параметры - пары имя-значение, которые используются для инициализации значений свойств соответствующего валидатора.

Есть три способа указать Validator в правиле проверки. Во-первых, Validator может быть именем метода в классе модели данных, аналогично authenticate в примере выше. Метод проверки оформляется следующим образом:

/**
 * @param string the name of the attribute to be validated
 * @param array options specified in the validation rule
 */
public function ValidatorName($attribute,$params) { ... }

Второй способ - указать Validator в качестве имени класса. В этом случае для проверки данных в момент применения правила создается экземпляр класса проверки. Дополнительные параметры в правиле используются для инициализации значений атрибутов экземпляра. Класс проверки должен быть производным классом от CValidator.

Примечание: Задавая правила для модели Active Record, мы можем использовать специальный параметр on. Этот параметр может быть типа 'insert' или 'update', и правило применяется соответственно в случае добавления или обновления записи. Если параметр не задан, то правило применяется в обоих случаях, когда вызывается save().

Третий вариант - предопределить псевдоним класса валидатора. В примере выше, имя required - это псевдоним класса CRequiredValidator, который проверяет, чтобы проверяемое значение атрибута не было пустым. Ниже приведен полный список предопределенных псевдонимов валидаторов, включенных в состав Yii:

  • boolean: псевдоним класса CBooleanValidator, который проверяет, чтобы атрибут имел значение либо CBooleanValidator::trueValue либо CBooleanValidator::falseValue;

  • captcha: псевдоним класса CCaptchaValidator, который проверяет, чтобы значение атрибута было равно коду верификации на капче;

  • 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.

Ниже представлены несколько примеров использования предопределенных валидаторов:

// имя пользователя - обязательное поле формы
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'),

3. Безопасное присваивание значений атрибутам

Примечание: возможность присвоения значения атрибуту в зависимости от используемого сценария доступна, начиная с версии 1.0.2.

После того, как создан экземпляр модели данных, нам часто требуется заполнить его данными, которые ввел пользователь. Это можно проделать легко и непринужденно, используя массовое присваивание:

$model=new LoginForm;
$model->scenario='login';
if(isset($_POST['LoginForm']))
    $model->attributes=$_POST['LoginForm'];

Примечание: свойство scenario стало доступно, начиная с версии 1.0.4. В процессе массового присваивания данное свойство используется для определения атрибутов, значения которых могут быть массово присвоены. В версиях 1.0.2 и 1.0.3 массовое присваивание в конкретном сценарии осуществляется следующим образом:

$model->setAttributes($_POST['LoginForm'], 'login');

Последнее выражение в примере как раз и является массовым присваиванием, где значение каждой переменной в $_POST['LoginForm'] присваивается соответствующему атрибуту модели в сценарии login. Это эквивалентно следующей операции:

foreach($_POST['LoginForm'] as $name=>$value)
{
    if($name является безопасным атрибутом)
        $model->$name=$value;
}

Безопасность входных данных задается при помощи метода safeAttributes и конкретного сценария. По умолчанию метод возвращает как безопасные все публичные переменные-члены для CFormModel и все поля таблицы, кроме первичного ключа, - для CActiveRecord. Мы можем переопределить этот метод, чтобы ограничить безопасные атрибуты в соответствии с требованиями сценария. Например, модель, описывающая пользователя, может содержать множество различных атрибутов, но в сценарии авторизации нам требуются только атрибуты username и password. Обозначить это можно следующим образом:

public function safeAttributes()
{
    return array(
        parent::safeAttributes(),
        'login' => 'username, password',
    );
}

Будет аккуратнее, если структуру значения, возвращаемого методом safeAttributes оформить так:

array(
   // эти атрибуты могут быть *массово присвоены* в любом сценарии,
   // кроме специально обозначенных ниже
   'attr1, attr2, ...',
     *
   // эти атрибуты можно *присвоить массово* только в сценарии 'scenario1'
   'scenario1' => 'attr2, attr3, ...',
     *
   // а эти - только в сценарии 'scenario2'
   'scenario2' => 'attr1, attr3, ...',
)

Если модель не завязана на сценарии (т.е. используется только в одном сценарии или же все сценарии работают с единым набором безопасных атрибутов), тогда возвращаемое значение можно упростить до одной строки:

'attr1, attr2, ...'

В случае небезопасных входных данных, мы должны присваивать значения соответствующим атрибутам, используя отдельные операции присваивания, как представлено ниже:

$model->permission='admin';
$model->id=1;

4. Выполнение проверки

Как только модель заполнена пользовательскими данными, вызываем метод CModel::validate(), чтобы запустить процесс проверки. По итогам проверки метод возвращает положительный или отрицательный результат. Для модели CActiveRecord проверку можно выполнять автоматически в момент вызова метода CActiveRecord::save().

При вызове метода CModel::validate() мы можем указать дополнительным параметром сценарий, и тогда правила проверки будут применяться только при выполнении соответствующего сценария. Правило применяется к сценарию тогда, когда опция on правила не установлена или содержит имя этого сценария. Если же при вызове CModel::validate() сценарий не указывать, то применяться будут только те правила, опция on которых не установлена.

Например, в момент регистрации пользователя для осуществления проверки мы выполняем следующее действие:

$model->scenario='register';
$model->validate();

Примечание: свойство scenario стало доступно, начиная с версии 1.0.4. Метод проверки использует данное свойство для определения правил проверки, которые будут применены. В версиях 1.0.2 и 1.0.3 проверка, ассоциированная со сценарием, осуществляется следующим образом:

$model->validate('register');

Можно указать правила проверки в классе модели формы таким образом:

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password_repeat', 'required', 'on'=>'register'),
        array('password', 'compare', 'on'=>'register'),
    );
}

В результате, первое правило будет распространяться на любые сценарии, а два последующих будут применяться только к сценарию register.

Примечание: возможность применения правил проверки в зависимости от используемого сценария доступна, начиная с версии 1.0.1.

5. Информация об ошибках

Чтобы узнать, возникли ли во время выполнения проверки какие-либо ошибки, можно воспользоваться методом CModel::hasErrors(). И если ошибки действительно есть, то получить их можно с помощью метода CModel::getErrors(). Оба метода могут быть использованы как для всех, так и для конкретного атрибута.

6. Метки атрибутов

Часто при работе с формами для каждого поля требуется отображать его метку. Она подсказывает пользователю, какие данные ему требуется ввести в поле. Мы, конечно, можем задать метки полей в представлении, но, если указать их непосредственно в модели данных, мы выиграем в удобстве и гибкости.

По умолчанию CModel в качестве меток возвращает названия атрибутов. Изменить их можно, если
переопределить метод attributeLabels().

Далее мы увидим, что возможность указания меток в модели данных позволяет быстро создавать сложные формы.