I'm creating a form so users can change their passwords. This form is in my settings controller, but I'm saving the data to my users table.
I have the following form
settings/index.ctp
echo $this->Form->create('settings');
echo $this->Form->input('current_password');
echo $this->Form->input('password');
echo $this->Form->input('repass', array('type'=>'password', 'label'=>'Re-Enter Password'));
echo $this->Form->end(__('Submit'));
Here's my Setting model
function equalToField($array, $field) {
print_r($array); //check to see if it was even being triggered...it's not!
return strcmp($this->data[$this->alias][key($array)], $this->data[$this->alias][$field]) == 0;
}
public function beforeSave() {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
public $validate = array(
'password' => array(
'required' => array(
'rule' => array('minLength', '8'),
'message' => 'A password with a minimum length of 8 characters is required'
)
),
'repass' => array(
'required' => array(
'rule' => array('equalToField', 'password'),
'message' => 'Passwords do not match'
)
)
);
And the code in my SettingsController to save it
$password = Security::hash($this->request->data['settings']['current_password'], NULL, true);
$this->loadmodel('User');
$options = array('conditions' => array('User.' . $this->User->primaryKey => AuthComponent::user('id')));
$user = $this->User->find('first', $options);
if($user['User']['password'] == $password){ //current password match
$this->User->id = AuthComponent::user('id');
$this->User->saveField('password',Security::hash($this->request->data['settings']['password'], NULL, true));
}
else{
$this->Session->setFlash('Current password is incorrect');
}
What am I doing incorrectly that the validation isn't triggering? I'd prefer to keep this in my SettingsController if possible. Also, before anyone mentions it I plan on making the current password match into one of the validation criteria...just as soon as I get it working.
Update - I decided to do some digging around
In /lib/Model/Model.php I went to the validator function and printed the validator object, here's what I found
([validate] => Array (
[password] => Array (
[required] => Array (
[rule] => Array (
[0] => minLength
[1] => 8 )
[message] => A password with a minimum length of 8 characters is required ) )
[repass] => Array (
[required] => Array (
[rule] => Array (
[0] => equalToField
[1] => password )
[message] => Passwords do not match
) ) )
[useTable] => settings
[id] =>
[data] => Array (
[Setting] => Array (
[settings] => Array (
[current_password] => current_pass
[password] => testpass1
[repass] => testpass2
) ) )
I'm not sure if that's what I want, but it's using settings table for this and I'm saving to the users table. I changed that value to users (by manually setting the value in that function), but it didn't change anything.
When I use the following as was suggested, it pulls the validation from the UserModel not Settings
$this->User->set($this->request->data);
if($this->User->Validates() == true){
Ok, let's start with a fresh, new answer. There are various problems here.
You are defining the validation rules and stuff on the Setting
model, but in your code snippet you are using the User
model for saving the data, so if at all, it would apply the validation rules defined on the User
model. So while it's possible to use a different model for validation than for saving, I wouldn't recommend that, I would instead use the User
model for all these jobs.
That means, put the validation rules and methods in your User
model (that is hopefully linked to the users
table).
You are using lowercase/plural notation for model names, where it should be singular starting with a capital letter:
$this->Form->create('Setting');
$this->request->data['Setting']
Please note that this is just for demonstration purposes, as mentioned above you should use the User
model, ie:
$this->Form->create('User');
$this->request->data['User']
Model::saveField()
only validates the data that was explicitly passed to it, ie it would only validate the password
field, and you couldn't access any other fields from your validation methods since the data is not set on the model.
So instead you'll either have to use Model::save()
with a list of allowed fields supplied (you could also utilize Model::set()
):
$this->User->id = AuthComponent::user('id');
$this->User->save($this->request->data, true, array('password', 'repass'));
or you do trigger validation manually:
$this->User->id = AuthComponent::user('id');
$this->User->set($this->request->data);
if($this->User->validates() === true)
{
$this->User->saveField('password', Security::hash($this->request->data['User']['password'], NULL, true));
}
Note that in this scenario you could also use a different model for validation, ie:
$this->Setting->set($this->request->data);
if($this->Setting->validates() === true)
{
$this->User->saveField('password', Security::hash($this->request->data['User']['password'], NULL, true));
}
However, I think it's better to use the User model, if necessary change validation rules dynamically so that they fit your needs. For example by removing rules:
$this->User->validator()->remove('field_x')
->remove('field_y')
->remove('field_z');
Or by overriding the validation rules:
$this->User->validate = array
(
...
);
However, both is best to be done from within the model.
Model::saveField()
Last but not least, the Model::saveField()
method has 3 parameters, where the third one is a either a boolean
value (defaults to false
), defining whether or not to use validation, or an array
that takes the options validate
, callbacks
and counterCache
.
So you would have to set true
as the third parameter in order for validation being triggered by Model::saveField()
at all:
$this->User->saveField('password', Security::hash($this->request->data['User']['password'], NULL, true), true);
ps. are you aware that you are currently hashing twice? First directly when passing the data to Model::saveField()
, and then a second time in Model::beforeSave()
.