Understanding saveMany and validation errors

    $data = $this->MyModel->newEntities($theData);
    $result = $this->MyModel->saveMany($data);

$theData in this example contains a varying number of entities. If any of the entities fail validation. saveMany returns false, however the entities that passed validation still get saved.

In the context of my application this is somewhat beneficial as it saves data even if some of it is invalid, but I imagine this will lead to unpredictable results.

What is a recommended approach to dealing with a saveMany and is it good practice for entities with errors to be let slip through?

It seems like 2 very powerful functions doing a lot of work behind the scenes without much control.

I suppose what I’d like is for valid entities to be saved, then the user to get a message…

5 records were invalid - reasons:
1. bla bla
2. bla bla
....

Right now my app either saves all entities perfectly, or saves some and returns false, which makes it quite difficult to provide further information.

Thanks for any insight.

What database are you using, and does the table you’re writing to support transactions? saveMany should happen in a transaction, and roll back all saves if any fail.

Thanks for the reply - I think this might have been resolved in 4.0.5

https://bakery.cakephp.org/2020/03/28/cakephp_405_released.html

“Table::saveMany() now correctly rollbacks a transaction when an entity other than the first fails to save because of application rules or database failure.”

I will update and see - I’m pretty sure my version of MySQL (5.0.2) does, it’s using the InnoDB Storage Engine.

That said - what is an appropriate way of handling errors with saveMany? I tried debugging getErrors and hasErrors but it just throws Call to a member function hasErrors() on array

$data = $this->MyModel->newEntities($theData);
debug($data->getErrors()); // here?
$result = $this->MyModel->saveMany($data);

Yep updating to 4.0.5 fixed it. I’m using:

    $myData = $this->MyModel->newEntities($theData);
    foreach($myData as $k => $v) {
        if($v->hasErrors()) {
            unset($myData[$k]);
        }
    }
    $result = $this->MyModel->saveMany($myData);

To remove entities that aren’t valid, which leaves valid entities and it saves perfectly.

Note that what you have will remove entities that fail validation. But rules are not run until it tries to save, so it’s still possible that what you have here will fail. What you want in this case may not be saveMany, but rather just your own loop that tries to save each one separately and ignores any errors that may happen with each one.

1 Like