DCurrentPassword: Валидация текущего пароля в Yii

Форма подтверждения пароля

Программирование

Дмитрий Елисеев

В одном из проектов потребовалось ввести в форму изменения настроек профиля пользователя поле «Введите текущий пароль». Сначала был быстро сделан валидирующий метод в самой модели пользователя. В последующем для повторного использования в разных проектах было решено написать полноценный настраиваемый внешний валидатор.

В примитивном виде это сначала выглядело так:

class User extends CActiveRecord
{
    public $old_password;
 
    // ...
 
    public function rules()
    {
        return array(
            // ...
            array('old_password', 'required', 'on'=>'settings'),
            array('old_password', 'currentPassword', 'on'=>'settings'),
        ));
    }
 
    protected function currentPassword($attribute,$params)
    {
        if (!$this->validatePassword($this->$attribute)
            $this->addError($attribute, 'Неверный текущий пароль');
    } 
 
    // ...
}

Сценарий «settings» использовался только на странице настроек в личном кабинете пользователя. Там валидатор обеспечивал проверку правильности вводимого в поле $old_password текущего пароля. Администратору, соответственно, при редактировании профилей пользователей пароль вбивать не приходилось.

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

Код класса на GitHub

Поместите класс в каталог /protected/components/ и используйте в необходимой модели.

Минимальный вызов валидатора:

class User extends CActiveRecord
{
    public $old_password;
    public $new_password;
 
    // ...  
    public function rules()
    {
        return array(       
            // ...          
            // Settings
            array('old_password', 'required', 'on'=>'settings'),
            array('old_password', 'DCurrentPassword', 'on'=>'settings'), 
        ));
    }
 
    public function validatePassword($password)
    {
        return $this->hashPassword($password) === $this->password;
    }
}

Здесь валидатор используется прямо в модели пользователя. Нужно только при необходимости указать имя проверяющего пароль метода. Но если мы используем валидатор в форме, то нужно указать и непосредственно ActiveRecord класс пользователя. Если нужно просить текущий пароль только для некоторых данных, то массив этих полей нужно перечислить в параметре dependsOnAttributes.

Полный код вызова из CFormModel (за исключением указания правил стандартных полей модели) может выглядеть так:

class UserForm extends CFormModel
{
    public $id // для хранения идентификатора пользователя
 
    // поля модели User
    public $login;
    public $email;
    public $birthday;
    // ...
 
    // дополнительные поля для новых данных
    public $old_password; // поле 'Введите текущий пароль'
    public $new_password; // поле 'Введите новый пароль'
    public $new_confirm;  // поле 'Подтвердите новый пароль'
    public $new_email;  // поле 'Введите новый Email'
 
    public function rules()
    {
        return array(
 
            // ...
 
            array('old_password, new_email, new_password, new_confirm', 'length', 'max'=>255),
            array('new_email', 'email', 'message'=>'Неверный формат E-mail адреса'),            
            array('new_password', 'length', 'min'=>6, 'allowEmpty'=>true),
            array('new_confirm', 'compare', 'compareAttribute'=>'new_password', 'message'=>'Пароли не совпадают'),
 
            // Settings
            array('old_password', 'DCurrentPassword', 
                // будем работать только в сценарии 'settings'
                'on'=>'settings',
                // имя CActiveRecord класса модели пользователя
                'className'=>'User',
                // срабатываем только если ввели новый пароль или email
                'dependsOnAttributes'=>array('new_password', 'new_email'), 
            ),
        ));
    }
 
    public function attributeLabels()
    {
        return array(
            'login'=>'Логин',
            'email'=>'Email',
            'birthday'=>'Дата рождения',
            // ...
            'old_password'=>'Текущий пароль',
            'new_password'=>'Новый пароль',
            'new_confirm'=>'Подтверждение нового пароля',
            'new_email'=>'Новый Email',
        );
    }
}

До валидации формы полю UserForm::id необходимо присвоить значение первичного ключа валидируемой модели. Именно по этому ключу валидатор будет искать нужного пользователя для проверки пароля.

protected function actionSettings()
{
    // загружаем модель текущего пользователя
    $user = $this->loadUserModel();
    $user->scenario = 'settings'; 
 
    // создаём форму с нужным сценарием для правил валидации
    $form = new UserForm(); 
    $form->scenario = 'settings'; 
 
    // Присваиваем идентификатор пользоателя форме 
    $form->id = $user->getPrimaryKey(); 
 
    if (isset($_POST['UserForm'])
    {   
        $form->attributes = $_POST['UserForm'];
        if ($form->validate())
        {       
            $user->attributes = $_POST['UserForm'];
            if ($user->save())
            {
                Yii::app()->user->setFlash('settings-form', 'Сохранено!');
                $this->refresh();
            }
        }
    }
    $this->render('settings', array('model'=>$form);
}

На этом всё. Теперь форма будет запрашивать текущий пароль только при заполнении полей «Новый пароль» или «Новый Email».

Комментарии

Войти | Завести аккаунт


(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>