使用CakePHP触发模型验证

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.

1. Mixing up models

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


2. Lowercase/plural notation for model names

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']

3. Validation is applied to explicitly passed data only

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.


4. Triggering validation with 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().