Validating multiple models at once

There are forms where you want to add/edit not only fields of the current model, but also of a related one (usually hasMany or belongsTo).

Example:
User and Post

$this->Post->set($this->data);
$this->Post->User->set($this->data);

$val1 = $this->Post->validates();
$val2 = $this->Post->User->validates();

if ($val1 && $val2) { 
    // OK (save both models separatly in order to use the user_id)
    $this->Post->User->save(null, false);
    $this->data['Post']['user_id'] = $this->Post->User->id;
    $this->Post->save($this->data, false);
} else { 
    //ERROR 
}

So whats going on here?
We first set the posted data to the models wie want to validate. This is important when using validates() instead of save(). Then we validate them and only if both pass we continue saving.

Why not directly? Why using $val1 and $val2?
Well, for that you have to know something about php as programming language and how conditions are processed.
If (condition1 && condition2 && ...) {} else {} stops checking conditions as soon as the first condition fails. this is "smart" because it saves time. If the first condition fails, it will jump to the else block no matter what the other conditions return. So why bothering to check them?
But we want to validate both models no matter what – so we have to process them before and only compare the return values! If you miss that you won’t see the validation errors of the second model in the view (if the first one failed, anyway).

Cake automatically passes the errors to the view, if the Model.field syntax is used there:

echo $this->Form->input('User.email');
echo $this->Form->input('User.username');
echo $this->Form->input('comment'); // or Post.comment

Thats it. You can extend this with as many models as you like.

Using PHP Tricks

If you read my other article you might have found another approach:

if ($this->Post->validates() & $this->Post->User->validates()) {}

This works because one & will first check all conditions before deciding what to do.

To summarize: NEVER use two & (&&) in this situation – for validating multiple models at once. It’s wrong!

Using cake’s find(all)

If your data is in model + related models structure you can also use this approach. It is faster and shorter (but I sometimes like control over my form processing):

if ($this->Post->saveAll($data, array('validate' => 'only'))) {
    //saveAll with validate false or single set of saves with validate false
}

This will only validate all the inputs without saving it.
Either way you should then set validate to false afterwards (no need to re-validate the data on save).

1 Comment

  1. that’s exactly what I needed, thanks a lot!

    today you saved me a headache! jejejeje

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.