CakePHPのpatchEntity()とisDirty()

小ネタ。ちょっとハマったので。

「patchEntityをして、差分があったときだけsave()に入る」みたいな処理を書きたかった。cake3始めたばかりの頃。

$post = $this->PostsTable->get(1);
$post = $this->PostsTable->patchEntity($post, $this->request->getData();
if ($post->isDirty()) {
    $this->PostsTable->saveOrFail($post);
}

のような。 問題は、 isDirty()の挙動というか。

dirty となるのは 正しく、更新されたフィールドがあるとき`である。
何を意味するか。
すなわち、
更新しようと思ったフィールドが、全て「適切でなかったとき」、ditryとはならない** 。

たとえば、「ブログ(post)のタイトルを更新しようとした「posts.titleには「最大30文字」のバリデーションがかかっている」「40字のタイトルを付けて更新ボタンをクリック!」というケース。 これは、 marshal() が完了する前に拒絶され、「patchEntity()の結果得られる$post->title」は 以前のままである。その点において、isDirty()はfalseのまま。 (むしろ汚れてるけどねー)

Table::save() の中を追うと、 「isNew()である」もしくは「isDirty()である」のチェックが行われている。なので、「更新すべきフィールドがあったら〜」なんて処理、思い切ってsave()にたくしてしまって良い。

$post = $this->PostsTable->get(1);
$post = $this->PostsTable->patchEntity($post, $this->request->getData();
try {
    $this->PostsTable->saveOrFail($post);
} catch (\Cake\ORM\Exception\PersistenceFailedException $e) {
    $entity = $e->getEntity();
    $errors = $entity->getErrors();
    // $errorsをさばく
}

くらいなもんである。