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.php か AppServiceProvider で登録します。
// 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システム開発のご相談はお気軽にどうぞ。