I had some troubles with the password confirmation field for when adding
updating user records, so i thought that i should share the way i got it
working.
The scenario is the basic one, you have a database table (say user) and this
table has a field called password, which is a sha1/md5/etc hash of the user
password.
This is the workflow:
When you create a new user, the password needs to be hashed and saved, but when
you update a user record, if the same scenario happens, we end up with a hash of
the user hashed password, and we don't want this. Instead, on update, we will
empty the user password from the model object, store it temporary in another
variable then check to see if the password has been submitted in the form, if it
was, it means the user password needs to be updated, therefore we need to hash
the password(which is plain text now), if it wasn't submitted, then it means it
doesn't need to be updated therefore, we restore it from the temporary variable.
So, here we go, the model:
~~~
[php]
<?php if ( ! defined('YII_PATH')) exit('No direct script access allowed');
class User extends CActiveRecord
{
// holds the password confirmation word
public $repeat_password;
//will hold the encrypted password for update actions.
public $initialPassword;
/**
* @return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
//password and repeat password
array('password, repeat_password', 'required',
'on'=>'resetPassword, insert'),
array('password, repeat_password', 'length', 'min'=>6,
'max'=>40),
array('password', 'compare',
'compareAttribute'=>'repeat_password'),
);
}
public function beforeSave()
{
// in this case, we will use the old hashed password.
if(empty($this->password) && empty($this->repeat_password)
&& !empty($this->initialPassword))
$this->password=$this->repeat_password=$this->initialPassword;
return parent::beforeSave();
}
public function afterFind()
{
//reset the password to null because we don't want the hash to be shown.
$this->initialPassword = $this->password;
$this->password = null;
parent::afterFind();
}
public function saveModel($data=array())
{
//because the hashes needs to match
if(!empty($data['password']) &&
!empty($data['repeat_password']))
{
$data['password'] =
Yii::app()->user->hashPassword($data['password']);
$data['repeat_password'] =
Yii::app()->user->hashPassword($data['repeat_password']);
}
$this->attributes=$data;
if(!$this->save())
return CHtml::errorSummary($this);
return true;
}
}
~~~
When the user is created, we do it with the "insert" scenario, meaning
that the password is required, but when we update it, we do it with the
"update" scenario, meaning that the password is not required anymore,
therefore when the form is submitted, the password fields can be empty, and the
validation won't fail. This allows us to restore the hashed password from the
temporary variable.
Just as a side note, here is how my controller methods looks:
~~~
[php]
public function actionCreate()
{
$user=new User('insert');
$this->saveModel($user);
$this->setViewData(compact('user'));
$this->render('create', $this->getViewData());
}
public function actionUpdate($id)
{
$user=$this->loadModel($id);
$user->scenario='update';
$this->saveModel($user);
$this->setViewData(compact('user'));
$this->render('update', $this->getViewData());
}
protected function saveModel(User $user)
{
if(isset($_POST['User']))
{
$this->performAjaxValidation($user);
$msg = $user->saveModel($_POST['User']);
//check $msg here
}
}
~~~
And this is the rendering form:
~~~
[php]
<section>
<?php echo $form->labelEx($user,'password'); ?>
<div>
<?php echo
$form->passwordField($user,'password',array('maxlength'=>40)); ?>
<?php echo
$form->passwordField($user,'repeat_password',array('maxlength'=>40));
?>
</div>
<?php echo $form->error($user,'password'); ?>
</section>
~~~
Hope it helps :)
[Chinese
Version](http://www.yiiwiki.com/wiki/view/id/20/title/%E6%A8%A1%E5%9E%8B%E4%B8%AD%E7%9A%84%E7%A1%AE%E8%AE%A4%E5%AF%86%E7%A0%81%E5%AD%97%E6%AE%B5
"")