対象バージョン: Laravel 13.x / PHP 8.3+(2026年時点のシリーズ基準)
一次ソース: Events
注文が完了した、ユーザーが登録した——こうした「アプリ内の出来事」に対して、メール送信やログ記録など複数の後処理を直書きすると、コントローラーやサービスが肥大化し、変更のたびに影響範囲が広がります。Laravel のイベントとリスナーは、出来事(イベント)と反応(リスナー)を分離し、1つのイベントに複数の処理をぶら下げられる仕組みです。この記事では、なぜ使うか、どう作るか、どう登録・配送するか、キュー化と運用メモまでを Laravel 13.x の公式手順に沿って整理します。
この記事で分かること
- イベント/リスナーを使う理由(疎結合と複数反応)
- Laravel 13.x での作成(
make:event/make:listener --event) - 登録の主経路(Event Discovery)と任意の手動登録(
Event::listen) - ディスパッチ(
::dispatch/event())と確認(event:list) - キュー可能なリスナー(
ShouldQueue)の要点 - 向き・不向き(利用シーン)と運用メモ
なぜイベントとリスナーを使うか
Laravel のイベントは、アプリケーション内で起きる事象をクラスとして表現します。リスナーはその事象への反応を定義します。公式ドキュメントが示すとおり、これはシンプルなオブザーバーパターンの実装です(Events)。
典型例は「注文が出荷された」ときに Slack 通知やメールを送るケースです。出荷処理のコードに通知コードを埋め込む代わりに、OrderShipped イベントを発火し、通知用リスナーが受け取ります。出荷ロジックと通知ロジックが互いに依存しないため、あとから「監査ログも残す」といったリスナーを追加しても、出荷側の変更は不要です。
向いていること
- 1つのアクションのあとに、互いに独立した複数の後処理がある
- ドメインの「事実」を明示し、呼び出し側を薄く保ちたい
向いていない/別手段がよいこと
- 定期的な DB バックアップなど、時刻駆動の処理 → タスクスケジューラ向き
- 単一モデルのライフサイクルフックだけが必要 → Eloquent オブザーバーで足りる場合がある
- 「あとで重い処理をまとめて実行」が主目的で、ドメインイベントとしての意味が薄い → Job の直接ディスパッチで十分な場合がある
本記事では、イベントとしての意味がはっきりしている「出荷完了 → 通知」系を主例にします。
対象バージョンと登録方法の変遷
本記事の前提は Laravel 13.x / PHP 8.3+ です。Laravel 13 は 2026年3月17日リリースの現行メジャーで、サポート表上 PHP 8.3〜8.5 を対象とします(Release Notes)。
| Laravel | イベント/リスナー登録の主経路 |
|---|---|
| 10.x 以前の典型 | app/Providers/EventServiceProvider.php の $listen 配列 |
| 11.x / 13.x(現行) | Event Discovery(app/Listeners を自動スキャン)。手動なら AppServiceProvider で Event::listen |
fresh な Laravel 13 スケルトンには EventServiceProvider 自体がありません。旧記事で見かける $listen = [ OrderShipped::class => [SendShipmentNotification::class] ] は、10.x 向けの手順です。現行で同じ手順を「必須」として書くと、ファイルが無くて詰まります。
手動で明示登録したい場合の現行経路は、AppServiceProvider::boot での Event::listen です。本記事は公式どおり Discovery を主、手動登録を補助として扱います。
イベントの作成
イベントクラスは、出来事に必要なデータを運ぶコンテナです。Artisan で生成します。
php artisan make:event OrderShipped
生成後、コンストラクタで公開プロパティを受け取る形が一般的です(公式例も同趣旨)。
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderShipped
{
use Dispatchable, SerializesModels;
public function __construct(
public Order $order,
) {}
}
Dispatchable により後述の OrderShipped::dispatch(...) が使えます。SerializesModels は、キュー化されたリスナーで Eloquent モデルを安全に直列化するときに役立ちます。
リスナーの作成
リスナーはイベントへの反応です。--event で対象イベントを指定すると、handle の型ヒントまで揃えてくれます。
php artisan make:listener SendShipmentNotification --event=OrderShipped
生成されたリスナーの骨子は次のような形です。
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
class SendShipmentNotification
{
public function handle(OrderShipped $event): void
{
// $event->order を使って通知・メール送信など
}
}
1つのイベントに複数リスナーを付けられます。例: 通知用と監査ログ用を別クラスにする。Discovery は handle または __invoke で始まるメソッドを、型ヒントされたイベントのリスナーとして登録します。
登録(Event Discovery が既定)
Laravel 13.x では、既定で app/Listeners をスキャンしてリスナーを自動登録します。make:listener --event=... で型ヒントが付いていれば、追加の $listen 配列は不要です。
登録内容の確認:
php artisan event:list
本番ではデプロイ時に php artisan event:cache(または optimize)でリスナー一覧をキャッシュすると起動が速くなります。クリアは event:clear です。
任意: 手動登録(AppServiceProvider)
Discovery 以外のディレクトリやクロージャで聴きたい場合は、AppServiceProvider::boot で明示できます。
use App\Events\OrderShipped;
use App\Listeners\SendShipmentNotification;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(
OrderShipped::class,
SendShipmentNotification::class,
);
}
リスナーを別ディレクトリに置く場合は、bootstrap/app.php の withEvents(discover: [...]) でスキャン対象を広げられます。
歴史メモ: Laravel 10.x では
EventServiceProviderの$listenが典型でした。アップグレード後に古い手順のまま探す必要はありません。
イベントのディスパッチ
処理の本体(例: 出荷確定)が終わったタイミングでイベントを発火します。公式の主例は静的 dispatch です。
use App\Events\OrderShipped;
// 出荷処理のあと
OrderShipped::dispatch($order);
event() ヘルパーでも同様です。
event(new OrderShipped($order));
どちらもイベントディスパッチャ経由で登録済みリスナーを呼び出します。条件付きなら dispatchIf / dispatchUnless もあります。
DB トランザクション内で発火し、コミット後にだけ届けたい場合は、イベント側で ShouldDispatchAfterCommit を実装する選択肢があります(詳細は公式 Events ドキュメント)。
キュー可能なリスナー(ShouldQueue)
メール送信や外部 API など時間がかかる反応は、同期実行だとリクエストを遅くします。リスナーに ShouldQueue を付けると、イベント発火時にリスナーがキューへ載り、ワーカーが非同期で処理します。
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
public function handle(OrderShipped $event): void
{
// メール送信など
}
}
前提として、キュー接続の設定と queue:work(または相当)の稼働が必要です。make:listener 生成クラスには ShouldQueue の import が含まれることが多いですが、implements は自動では付かないため、必要なリスナーだけ明示してください。
失敗時の failed メソッド、試行回数、トランザクション後キュー(ShouldQueueAfterCommit)などは公式の「Queued Event Listeners」を参照してください。本記事では導入のみに留めます。
利用シーンの具体例
シナリオ: ユーザーが商品を購入し、出荷が確定したときに通知を送る。
php artisan make:event OrderShippedphp artisan make:listener SendShipmentNotification --event=OrderShipped- (任意)重い通知ならリスナーに
ShouldQueueを付与 - 出荷処理の成功後に
OrderShipped::dispatch($order)(またはevent(new OrderShipped($order))) php artisan event:listでOrderShipped→SendShipmentNotificationを確認
同様に「新規登録 → ウェルカムメール」も、登録完了イベント+メール用リスナーの組み合わせが向いています。一方、定期バックアップはイベントではなくスケジューラの領域です。時刻で動かす処理をイベント例に混ぜると、読者が手段を取り違えやすいため、本記事では扱いません。
定期バックアップなどの時刻駆動処理の実装手順は、Laravelでタスクスケジューリングを実装する方法で整理しています。
運用メモ
- 確認: 開発中は
event:listで Discovery/手動登録の結果を見る - 本番:
event:cacheをデプロイ手順に含める - テスト:
Event::fake()でリスナー実行を止め、assertDispatched等で発火だけ検証できる(Events — Testing) - 購読者(Subscriber): 複数イベントを1クラスで束ねたい場合は Event Subscribers。Discovery 規約に沿うか、
Event::subscribeで登録 - スコープ: 本記事は Observer/Notification/Job の深掘りには踏み込みません。手段の選択は「ドメインの出来事を明示したいか」「非同期が必要か」で切り分けると迷いが減ります
まとめ
- Laravel 13.x では Event Discovery が既定。
EventServiceProvider::$listenは旧典型であり、fresh にはプロバイダ自体が無い make:event/make:listener --eventで骨格を作り、::dispatchまたはevent()で発火する- 重い処理はリスナーに
ShouldQueueを付け、キュー基盤とセットで使う - 利用シーンは「1事実 → 複数の独立した後処理」。定期処理はスケジューラへ
公式の詳細・属性によるキュー制御・Subscriber は Events(Laravel 13.x) を参照してください。