株式会社WR

株式会社WR

WEB TOTAL CONSULTING

LaravelのObserverパターン——モデルイベントで副作用を分離する
ブログ一覧へ
技術ブログ

LaravelのObserverパターン——モデルイベントで副作用を分離する

Laravelのモデルオブザーバーを使うと、「顧客登録時にウェルカムメール送信」「予約作成時に通知送信」といった副作用をコントローラーから分離できます。

Observerパターンとは——モデルイベントの概念

Laravelのモデルはデータ操作の際に自動的にイベントを発火します。

イベント タイミング
creating INSERT前
created INSERT後
updating UPDATE前
updated UPDATE後
saving INSERT/UPDATE前(両方)
saved INSERT/UPDATE後(両方)
deleting DELETE前
deleted DELETE後
restoring ソフトデリート復元前
restored ソフトデリート復元後

これらのイベントに処理を紐付けることで、「ユーザーが作成されたら歓迎メールを送る」「予約が確定したら在庫を減らす」といった副作用を宣言的に分離できます。


Observerクラスの作成

php artisan make:observer OrderObserver --model=Order
<?php

namespace App\Observers;

use App\Models\Order;
use App\Mail\OrderConfirmation;
use App\Jobs\UpdateInventory;
use Illuminate\Support\Facades\Mail;

class OrderObserver
{
    /**
     * 注文作成後の処理
     */
    public function created(Order $order): void
    {
        // 確認メールをキューで送信
        Mail::to($order->customer_email)
            ->queue(new OrderConfirmation($order));

        // 在庫更新ジョブをキューに投入
        UpdateInventory::dispatch($order)->onQueue('inventory');

        // Slackに通知
        $order->notifyAdminSlack();
    }

    /**
     * 注文ステータス更新後の処理
     */
    public function updated(Order $order): void
    {
        // ステータスが変わった場合のみ処理
        if ($order->isDirty('status')) {
            $order->sendStatusChangeNotification();
        }
    }

    /**
     * 注文削除前の処理
     */
    public function deleting(Order $order): void
    {
        // 関連する在庫を戻す
        if ($order->status !== 'cancelled') {
            $order->restoreInventory();
        }
    }
}

Observerの登録

Laravel 11では bootstrap/app.phpAppServiceProvider で登録します。

// app/Providers/AppServiceProvider.php
use App\Models\Order;
use App\Observers\OrderObserver;

public function boot(): void
{
    Order::observe(OrderObserver::class);
}

より実践的な例——ユーザー登録後の一連の処理

class UserObserver
{
    public function created(User $user): void
    {
        // 1. ウェルカムメール送信
        Mail::to($user->email)->queue(new WelcomeMail($user));

        // 2. デフォルト設定を作成
        $user->settings()->create([
            'notification_email' => true,
            'notification_slack' => false,
            'theme'              => 'light',
        ]);

        // 3. 管理者ダッシュボードのキャッシュをクリア
        Cache::forget('admin.user_count');
        Cache::forget('admin.recent_users');

        // 4. 分析イベントを記録
        activity()
            ->performedOn($user)
            ->log('ユーザー登録');
    }

    public function updated(User $user): void
    {
        // メールアドレスが変更された場合
        if ($user->isDirty('email')) {
            // 旧アドレスに変更通知
            Mail::to($user->getOriginal('email'))
                ->queue(new EmailChangedNotification($user));

            // 再認証を要求
            $user->email_verified_at = null;
            $user->saveQuietly(); // Observer の再帰呼び出しを防ぐ
        }
    }
}

withoutEvents——Observerを一時的に無効化

バッチ処理でObserverの副作用なしに大量データをインポートしたい場合は withoutEvents が使えます。

// Observer を無効にして高速バルクインサート
User::withoutEvents(function () use ($users) {
    foreach (array_chunk($users, 1000) as $chunk) {
        User::insert($chunk);
    }
});

// または saveQuietly() で個別に
$user->saveQuietly();

モデルイベント vs ジョブキュー vs イベント&リスナー

アプローチ 向いているケース
Observer モデルに密結合した副作用(関連データの生成など)
ジョブキュー 時間のかかる非同期処理
イベント&リスナー 複数のリスナーが必要・疎結合にしたい場合

ObserverとEvent/Listenerは目的が異なります。Observerはモデルのライフサイクルに特化しており、より宣言的に副作用を記述できます。


テスト

class OrderObserverTest extends TestCase
{
    use RefreshDatabase;

    public function test_注文作成時にメールがキューに入る(): void
    {
        Mail::fake();

        $order = Order::factory()->create(['status' => 'pending']);

        Mail::assertQueued(OrderConfirmation::class, function ($mail) use ($order) {
            return $mail->hasTo($order->customer_email);
        });
    }
}

まとめ

LaravelのObserverパターンを使うことで、モデルの副作用をコントローラーから切り離し、テストしやすい疎結合な設計を実現できます。弊社では予約システム・注文管理システムでObserverを積極的に活用しています。

LaravelによるWebシステム開発のご相談はお気軽にどうぞ。

Category 技術ブログ

Related Posts

関連記事

開発・技術のご相談はお気軽に

お見積りは無料です。まずはお気軽にご相談ください。

お問い合わせ →