🫥

Laravel で Observer を利用しようとして無駄にハマった件

2025/02/13に公開

概要

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\Modelupdate メソッドであり、その中で呼んでいる save の処理中にイベントを発火させています

    public function update(array $attributes = [], array $options = [])
    {
        if (! $this->exists) {
            return false;
        }

        return $this->fill($attributes)->save($options);
    }

一方、最初に書いてたこの👇コードだと、 Illuminate\Database\Eloquent\Builderupdate メソッドが呼ばれます。

User::where('active', 1)->update(['name' => '更新された名前']);

こちらの update メソッドは、 Illuminate\Database\Query\Builderupdate メソッドをラッパーしている形となり、イベント云々は関係なくなります = Observer は動きません。

    // Illuminate\Database\Eloquent\Builder.php
    public function update(array $values)
    {
        return $this->toBase()->update($this->addUpdatedAtColumn($values));
    }

まとめ

  • Modelupdate メソッドを使用すれば、Observer が起動する。
  • Builderupdate メソッドでは、Observer が起動しない。

Discussion