Laravel で Observer を利用しようとして無駄にハマった件
概要
Laravel で Observer を利用する開発案件があったのですが、フレームワークの仕様を勘違いしていてどつぼにハマったので記事にしました。
(Laravel のバージョンは11系です)
結論
Observer を利用するなら Eloquent モデルのインスタンスを生成して、そこからデータ操作するべし
本題
Laravel の Observer とは?
Laravel の Observer は、Eloquent モデルの特定のイベント(作成、更新、削除など)に対して自動的に処理を実行できる仕組みです。コードの整理や再利用性が向上するため、開発に役立つ強力な機能です。
ハマりポイント
私はデータ更新のイベントをフックに処理をしてほしかったのですが、 update
メソッドで Observer が起動しないという落とし穴にハマってしまいました。
該当のコードは以下のような感じです。
User::where('active', 1)->update(['name' => '更新された名前']);
なぜ動かないのか
よく考えたら当たり前のことだったんですが、そもそも Eloquent Model が発火するイベントに対して起動するのが Observer なので、 Eloquent モデルのインスタンス経由でデータ操作する必要がありました。
例えば以下のような処理があったとして、
$user = User::find(1);
$user->name = '新しい名前';
$user->update();
この時の update
メソッドは Illuminate\Database\Eloquent\Model
の update
メソッドであり、その中で呼んでいる save
の処理中にイベントを発火させています
public function update(array $attributes = [], array $options = [])
{
if (! $this->exists) {
return false;
}
return $this->fill($attributes)->save($options);
}
一方、最初に書いてたこの👇コードだと、 Illuminate\Database\Eloquent\Builder
の update
メソッドが呼ばれます。
User::where('active', 1)->update(['name' => '更新された名前']);
こちらの update
メソッドは、 Illuminate\Database\Query\Builder
の update
メソッドをラッパーしている形となり、イベント云々は関係なくなります = Observer は動きません。
// Illuminate\Database\Eloquent\Builder.php
public function update(array $values)
{
return $this->toBase()->update($this->addUpdatedAtColumn($values));
}
まとめ
-
Model
のupdate
メソッドを使用すれば、Observer が起動する。 -
Builder
のupdate
メソッドでは、Observer が起動しない。
Discussion