CakePHP3で作ったバッチがどういうわけかよくsave()で失敗してて、しかもExceptionすら吐かずにfalseだけ返すものだから
「一体原因は何なんだろ?」
って思うことがしょっちゅうあったので、その解決策を書いておく。検証したCakePHPのバージョンは3.6.13。
save()がよく失敗する時はsaveOrFail()を使うようにしよう
save()で失敗してfalseが返ってくる場合、実際にはExceptionを吐く条件が満たされていることが多い。個人的な体験談としては、save()しようとしたEntityに該当するTableにbuildRules()が設定されていて、外部キールールに引っかかって実際にはExceptionを吐く条件を満たしていたことがあった。
例えば、保存しようとするEntityに該当するTableに、buildRules()として
1 |
$rules->add($rules->existsIn('article_id', 'Articles')); |
等が書いてあったら要注意。PersistenceFailedExceptionがthrowされる可能性がありうる。
このように、Exceptionを吐き出しうるケースで実際にExceptionをthrowさせて検知する為にはsave()の代わりにsaveOrFail()を使おう。内部処理でやっていることはsave()と同じだが、Exceptionがあったらちゃんとthrowしてくれる堅実性があるので便利。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
... /** * Try to save an entity or throw a PersistenceFailedException if the application rules checks failed, * the entity contains errors or the save was aborted by a callback. * * @param \Cake\Datasource\EntityInterface $entity the entity to be saved * @param array|\ArrayAccess $options The options to use when saving. * @return \Cake\Datasource\EntityInterface * @throws \Cake\ORM\Exception\PersistenceFailedException When the entity couldn't be saved * @see \Cake\ORM\Table::save() */ public function saveOrFail(EntityInterface $entity, $options = []) { $saved = $this->save($entity, $options); if ($saved === false) { throw new PersistenceFailedException($entity, ['save']); } return $saved; } ... |
基本的な保存処理はsave()よりもsaveOrFail()のほうが良いのか?
ここまでの話の流れから察するに、
「基本的な保存処理はsave()よりもsaveOrFail()を使ったほうが良いのでは…」
と思わなくもない。
これに関しては、buildRules()で厳密に外部キーを見ているようなレコードを保存する時にだけsaveOrFail()を使うと良いと思った。そのように使い分けていれば、コードの読み手としては
「あー、このレコードは外部キーなどを厳密に考慮して保存する必要があるのね」
って解釈できるから。