LaravelのCacheで排他制御!処理中のカートをロックします

こんにちは、さるまりんです🐵

今回はいつもより具体的な記事です。
Laravelで「カートが処理中の間は他の操作をブロックしたい」というときに使えるコードをまとめてみました。

APIでカートの注文確定などの処理を行っていて、ユーザーが別画面でカートをいじってしまうと、データがおかしくなったり、意図しない動作になることがあります。
そんな時にLaravelのCacheを使って「カートにロックをかける」方法を試してみました。

こんな場面で使えます

  • 注文ボタンを押してから決済完了までの間、カートをロックしたい
  • API処理中に同時にカート編集・追加されるのを防ぎたい
  • フロント側で「カートは現在処理中です」と案内したい

Cacheを使ったロックの考え方

LaravelのCacheは、一時的なデータや状態管理に便利です。
今回はCache::add()を使って、「処理中なら他の操作をブロックする」しくみを作ります。

処理の流れ

  1. カート処理開始 → ロックキーをCache::add()で作成
  2. 処理中は他のリクエストが同じキーを使って処理できない
  3. 処理完了時にロック解除(Cache::forget()

実装例:APIでカート処理を行う

public function processCart(Request $request)
{
    $userId = auth()->id();
    $lockKey = 'cart:lock:user:' . $userId;

    // 30秒間のロック(すでに処理中なら中断)
    if (!Cache::add($lockKey, true, 30)) {
        abort(423, 'Your cart is currently being processed. Please try again shortly.');
    }

    try {
        // 実際の処理(注文、計算など)
        $this->finalizeCart($userId);
    } finally {
        // 終了時にロック解除
        Cache::forget($lockKey);
    }

    return response()->json(['message' => 'Cart processed successfully']);
}

カート表示・アイテムの追加の処理でもチェック

表示処理
$lockKey = 'cart:lock:user:' . auth()->id();
$isLocked = Cache::has($lockKey);

return view('cart.index', [
    'cartItems' => $cartItems,
    'readOnly' => $isLocked,
]);
アイテム追加処理
$lockKey = 'cart:lock:user:' . auth()->id();

if (Cache::has($lockKey)) {
    abort(423, 'Your cart is currently being processed.');
}
// カートへの追加処理

Laravelありがとう:abort(423)でロック中を表示

操作中のカートにアクセスされた場合、次のようにabort(423)でブロックできます。

if (Cache::has('cart:lock:user:' . auth()->id())) {
    abort(423, 'Your cart is currently being processed.');
}

このコードで、カートが処理中ならHTTP 423 Lockedステータスでエラーページを表示します。

カスタムエラーページも

resources/views/errors/423.blade.phpを作っておくと、見た目も整えられます!

@extends('layouts.app')

@section('title', 'カート処理中です')

@section('content')
  <div class="container text-center">
      <h1>⏳ カートは現在処理中です</h1>
      <p>ただいまご注文の処理を行っています。しばらくしてからもう一度お試しください。</p>
      <a href="{{ url()->previous() }}" class="btn btn-primary">戻る</a>
  </div>
@endsection

処理中に別の操作が入り込むバグは追っかけにくくて厄介です。
Laravelのキャッシュをうまく使えば、こんな風に排他制御が実現できます!
もっといい方法があるかもしれないですが、今回はこの方法を採用しました。
さらに強固なロックについてはまた勉強してみたいと思います。

読んでくださってありがとうございました。

それではまた!