0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LaravelのJobでコンストラクタインジェクションを使ったらエラーになった話

Posted at

はじめに

LaravelでJobクラスを実装する際、Serviceクラスと同じ感覚でEloquentモデルをコンストラクタインジェクションしたところ、本番環境でジョブが失敗する事象に遭遇しました。

Serviceでは問題なく動いていたため、最初は理由が分からず悩みましたが、調査の結果、非同期処理特有の落とし穴であることが分かりました。

この記事では以下のポイントを解説します:

  • JobでEloquentモデルを渡すと起こる問題
  • Serviceとの違い
  • 安全なモデルの扱い方

問題が起きたコード

以下のように、JobクラスでEloquentモデルをそのまま受け取っています。

class SomeJob implements ShouldQueue
{
    use SerializesModels;

    public function __construct(
        public Hoge $hoge // ← Eloquentモデルを直接渡している
    ) {}

    public function handle()
    {
        $this->hoge->update([...]);
    }
}

このジョブは SomeJob::dispatch($hoge) でキューに投入されましたが、実行時にエラーで落ちました。


Serviceでは問題なかったのに、なぜJobだけ落ちたのか?

以下はServiceクラスの例です:

class SomeService
{
    public function process(Hoge $hoge)
    {
        $hoge->update([...]);
    }
}

こちらは正常に動作します。
その理由は、Serviceはすぐに同期実行されるのに対し、Jobは非同期で数分後などに実行されるためです。


Laravelの裏側で何が起きているのか?

LaravelのJobでは、SerializesModels トレイトを使うことで、Eloquentモデルをシリアライズする際にモデル本体ではなくそのIDのみを保存する仕組みになっています。

そのため、ジョブがキューから取り出されて実行される際には、IDをもとにモデルを再取得する処理が走ります。


Jobの流れを図解で説明

1. モデルを取得(Hoge::find(1))
2. dispatch() によりキューへ → IDだけが保存される
3. 数分後、Jobがワーカーで実行される
4. モデルIDを元に再取得 → 存在しないとnull
5. null->update() で実行時エラーが発生

例えば、該当のモデルが削除されていたり、関連が壊れていたりすると、find() に失敗して null が返り、エラーに繋がります。


回避策

方法①:IDだけ渡す(推奨)

class SomeJob implements ShouldQueue
{
    public function __construct(public int $hogeId) {}

    public function handle()
    {
        $hoge = Hoge::find($this->hogeId);

        if (! $hoge) {
            \Log::warning("Hoge not found");
            return;
        }

        $hoge->update([...]);
    }
}

呼び出し側も dispatch($hoge->id) に変えましょう。


方法②:モデルを渡すが null チェックを入れる

class SomeJob implements ShouldQueue
{
    use SerializesModels;

    public function __construct(public Hoge $hoge) {}

    public function handle()
    {
        if (! $this->hoge) {
            \Log::warning("Hoge was deleted before job execution");
            return;
        }

        $this->hoge->update([...]);
    }
}

この方法は柔軟ですが、レコードがなければ処理自体がスキップされるため、副作用を期待する処理では注意が必要です。


まとめ

比較項目 Service Job(Queue)
実行タイミング 即時実行 遅延・非同期
モデルの保持 メモリ上で保持 IDだけ保存し、あとで再取得
注意点 特になし モデルが削除されているとnullになる

おわりに

LaravelのJobでは、Serviceと同じ感覚でEloquentモデルを渡すと、実行タイミングのズレにより予期せぬエラーが発生することがあります。

非同期であるという前提を意識し、基本はIDを渡す設計にしておくことで、安全で再現性のある処理を構築できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?