#24 Laravel基礎

イベントとリスナーでコードを疎結合にする

イベントとリスナーとは

「ユーザーが登録されたとき」というイベントに対して、「ウェルカムメール送信」「ポイント付与」「ログ記録」など複数のリスナーが反応する仕組みです。

コントローラーに全部書くと依存が増えます。イベント・リスナーパターンを使うと、コントローラーは「イベントを発火する」だけでよくなります。


イベントクラスを生成する

php artisan make:event UserRegistered
<?php
// 📁 app/Events/UserRegistered.php

namespace App\Events;

use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserRegistered
{
    use Dispatchable, SerializesModels;

    public function __construct(
        public User $user  // ← イベントに含めるデータ
    ) {}
}

リスナークラスを生成する

php artisan make:listener SendWelcomeEmail --event=UserRegistered
php artisan make:listener AwardSignupPoints --event=UserRegistered
<?php
// 📁 app/Listeners/SendWelcomeEmail.php

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        Mail::to($event->user)->send(new WelcomeMail($event->user));
    }
}
<?php
// 📁 app/Listeners/AwardSignupPoints.php

namespace App\Listeners;

use App\Events\UserRegistered;

class AwardSignupPoints
{
    public function handle(UserRegistered $event): void
    {
        $event->user->increment('points', 100); // 登録ポイント付与
    }
}

イベントとリスナーを登録する

// 📁 app/Providers/EventServiceProvider.php

protected $listen = [
    \App\Events\UserRegistered::class => [
        \App\Listeners\SendWelcomeEmail::class,
        \App\Listeners\AwardSignupPoints::class,
    ],
];

Laravel 11以降は AppServiceProviderboot() で登録もできます。

// 📁 app/Providers/AppServiceProvider.php

use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(
        \App\Events\UserRegistered::class,
        \App\Listeners\SendWelcomeEmail::class,
    );
}

イベントを発火する

// 📁 app/Http/Controllers/Auth/RegisteredUserController.php

use App\Events\UserRegistered;

public function store(Request $request)
{
    $user = User::create([
        'name'     => $request->name,
        'email'    => $request->email,
        'password' => bcrypt($request->password),
    ]);

    // イベントを発火するだけ!
    // SendWelcomeEmail と AwardSignupPoints が自動で動く
    UserRegistered::dispatch($user);

    return redirect()->route('dashboard');
}

キューと組み合わせる

リスナーに ShouldQueue を実装するだけでキューで非同期実行できます。

// 📁 app/Listeners/SendWelcomeEmail.php

use Illuminate\Contracts\Queue\ShouldQueue;

class SendWelcomeEmail implements ShouldQueue
{
    // ...
}

モデルのイベントをディスパッチする

Eloquentモデルのイベントも同じ仕組みで扱えます。

<?php
// 📁 app/Models/Post.php

protected $dispatchesEvents = [
    'created' => \App\Events\PostCreated::class,
    'deleted' => \App\Events\PostDeleted::class,
];

まとめ

  • イベント:何かが起きたことを表すクラス(データを持つ)
  • リスナー:イベントに反応して処理を実行するクラス
  • EventServiceProvider$listen 配列で紐づけを登録する
  • UserRegistered::dispatch($user) でイベントを発火する
  • リスナーに ShouldQueue を付けるとキューで非同期実行できる

次回はコマンド(Artisanコマンドの自作)を学びます。