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. Плюс к этому, валидатор должен легко использоваться в других проектах с другими моделями. Вот что получилось:
Поместите класс в каталог /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».