3年前に書いた投稿の更新です(その投稿自体その4年前の投稿の更新です)。factoryからEloquentのインスタンスの作成も変わり、また新たな発見がありました。

まず、tinkerを使用して、isDirty()の復習です。
factory()を使って、1つレコードを作成します。

>>> User::factory()->create();
=> App\Models\User {#3632
     name: "村山 涼平",
     email: "ekoda.asuka@example.com",
     email_verified_at: "2022-06-12 20:18:11",
     #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
     #remember_token: "h7d19XhYd4",
     updated_at: "2022-06-12 20:18:11",
     created_at: "2022-06-12 20:18:11",
     id: 1,
   }

作成したレコードの名前を更新しますが、DBでなくオブジェクトのみの更新のために、fill()を使用します。

>>> $user = User::find(1);
=> App\Models\User {#3949
     id: 1,
     name: "村山 涼平",
     email: "ekoda.asuka@example.com",
     email_verified_at: "2022-06-12 20:18:11",
     #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
     #remember_token: "h7d19XhYd4",
     created_at: "2022-06-12 20:18:11",
     updated_at: "2022-06-12 20:18:11",
   }
>>> $user->fill(['name' => '山田太郎']);
=> App\Models\User {#3949
     id: 1,
     name: "山田太郎",
     email: "ekoda.asuka@example.com",
     email_verified_at: "2022-06-12 20:18:11",
     #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
     #remember_token: "h7d19XhYd4",
     created_at: "2022-06-12 20:18:11",
     updated_at: "2022-06-12 20:18:11",
   }

nameが変わりましたね。
実際、オブジェクトが更新したかどうかは、isDirty()を使います。

>>> $user->isDirty();
=> true

変更していますね。
項目ごとはどうでしょう?

>>> $user->isDirty('name');
=> true
>>> $user->isDirty('email');
=> false

nameの変更がありますが、emailの変更はなしです。正しいです。

ここで、変更したオブジェクトをもとに対応するレコードをDBに保存します。

>>> $user->save();
=> true

さて、この時点でのisDirty()の返り値は?

>>> $user->isDirty();
=> false
>>> $user->isDirty('name');
=> false

もう、isDirty()では、変更があったかどうかの判断はできません、何もDirtyではありません。

この時点、つまりオブジェクトの更新をDBに反映した時点でレコードが更新したかどうかを判断するには、isDirty()ではなく、wasChanged()を使用します。

>>> $user->wasChanged();
=> true
>>> $user->wasChanged('name');
=> true
>>> $user->wasChanged('email');
=> false

save()前のisDirty()の結果とまったく同じです。

実際には、こんなコードでwasChanged()は使われます。

...
$user->fill(['name' => '山田太郎']);

if ($user->isDirty()) {
    $user->save();
    if ($user->wasChagned('name')) {
        //nameが更新されたときのみ、以下のコードを実行
        ...
    }
}
...

さてさて、上の話は既存にあるレコードを更新したときの話です。新規のレコード作成のときもチェックしてみます。

>>> $user = User::factory()->make();
=> App\Models\User {#4588
     name: "浜田 京助",
     email: "tsubasa.saito@example.org",
     email_verified_at: "2022-06-12 20:21:59",
     #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
     #remember_token: "ZzZh0fHqJA",
   }

先ほどと違うのは、create()の代わりにmake()が使用されているところです。create()と違ってまだDBレコードは作成されていません。
この時点では、

>>> $user->isDirty();
=> true

更新と同じですね。
DBにレコードを保存してみます。

>>> $user->save();
=> true

さて、この時点でのisDirty()の返り値は?

>>> $user->isDirty();
=> false

期待通りです。
しかし、

>>> $user->wasChanged();
=> false

こちらは、更新時と違ってtrueとはなりません。wasChangedは訳せば更新したかという意味であり、作成されたかではないとすればfalseになるのは正しいのでしょう。

しかし、$userのようなインスタンスが既存のレコードを更新したのか、新規に作成されたのかが前もってわからないときの判断としてwasChanged()を使用できません。とかいってwasCreated()というメソッドもないのです。滅多に直面するケースは少ないですが、注意しましょう。

By khino