【PHP】Laravelでキャッシュを実装する方法

対象バージョン: Laravel 13.x / PHP 8.3+(2026年時点のシリーズ基準)
一次ソース: Cache / Release Notes

Laravel アプリでは、DB への重い問い合わせや時間のかかる計算結果を、しばらくのあいだメモリや高速ストアに置いて再利用することがよくあります。Laravel のキャッシュは、ファイル・データベース・Redis など複数のバックエンドを 同じ API で扱える仕組みです。この記事では、Laravel 13.x で Cache ファサードを使った基本操作、remember、ストア選択、アトミックロックまでを、現行の公式ドキュメントに沿って通します。

この記事で分かること

  • Laravel 13.x のキャッシュ設定(既定は database ストア)
  • Cache::get / put / remember / forever / forget の使い方
  • 名前付きストア(Cache::store)と cache() ヘルパー
  • Cache::touch による TTL 延長と Cache::flexible(stale-while-revalidate)
  • アトミックロック(Cache::lock)の最小例
  • よくある失敗(flush の影響範囲、タグ非対応ドライバ)

対象バージョンと前提

本記事の前提は Laravel 13.x / PHP 8.3+ です。Laravel 13 は 2026年3月17日リリースの現行メジャーで、サポート表上 PHP 8.3〜8.5 を対象とします(Release Notes)。調査時点で確認した laravel/framework は v13.34.0 です。

Laravel の基礎(インストール、ルーティング、コントローラ)は Laravel入門:インストールから簡単なサンプルアプリケーションの作成 を前提にできます。本記事はキャッシュ API に絞ります。


キャッシュ設定(config/cache.php と CACHE_STORE)

設定ファイルは config/cache.php です。どのストアを既定にするかは、通常 .env の CACHE_STORE で切り替えます。

Laravel 13.x の fresh スケルトンでは、既定は database です。

CACHE_STORE=database

database ドライバは、シリアライズした値をアプリのデータベースに保存します。fresh な Laravel 13 にはキャッシュ用テーブルのマイグレーション(0001_01_01_000001_create_cache_table.php)が含まれます。古いアプリでテーブルが無い場合は、次で作れます。

php artisan make:cache-table
php artisan migrate

そのほか公式がサポートする代表的なドライバは、file、redis、memcached、array(テスト向け)、null、storage、dynamodb、failover などです。ローカル学習では database または file、テストでは array が扱いやすいです。Redis / Memcached を使う場合は、各ドライバの前提(拡張や接続設定)を公式の Driver Prerequisites に従って用意します。


Cache ファサードの基本

操作の入口は Illuminate\Support\Facades\Cache です。

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class ReportController extends Controller
{
    public function summary(): array
    {
        $value = Cache::get('reports.summary');

        return [
            'summary' => $value,
        ];
    }
}

取得(get / has)

$value = Cache::get('key');
$value = Cache::get('key', 'default');

$value = Cache::get('key', function () {
    return DB::table('users')->count();
});

if (Cache::has('key')) {
    // キーが存在し、値が null でない
}

キーが無いとき get は null を返します。第2引数でデフォルト値やクロージャを渡せます。has は値が null のときも false になります。

保存(put / add / forever)

Cache::put('key', 'value', 10); // 10秒
Cache::put('key', 'value', now()->plus(minutes: 10));

Cache::add('key', 'value', 60); // 未存在のときだけ保存(原子的)

Cache::forever('key', 'value'); // 期限なし(手動で forget)

put に秒数を渡さないと無期限保存になります。add は既にキーがある場合は保存せず false を返します。

取得して無ければ計算(remember)

実務でいちばん使うのが remember です。キャッシュにあればそれを返し、無ければクロージャを実行して結果を保存します。

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

$users = Cache::remember('users', 60, function () {
    return DB::table('users')->get();
});

無期限版は rememberForever です。ヒットしたかどうかを知りたい場合は rememberWithWarmth を使えます(戻り値は [値, キャッシュヒットか])。

[$users, $warm] = Cache::rememberWithWarmth('users', 60, function () {
    return DB::table('users')->get();
});

削除(forget / flush)

Cache::forget('key');
Cache::flush(); // ストア全体をクリア

flush は設定したプレフィックスを尊重せず、そのストア上のエントリをまとめて消します。他アプリとストアを共有している場合は特に注意してください。


名前付きストアと cache() ヘルパー

複数ストアを使い分けるときは store メソッドに config/cache.php のストア名を渡します。

$value = Cache::store('file')->get('foo');
Cache::store('redis')->put('bar', 'baz', 600);

グローバルな cache() ヘルパーでも同じ操作ができます。

$value = cache('key');
cache(['key' => 'value'], 60);
cache()->remember('users', 60, function () {
    return DB::table('users')->get();
});

TTL の延長(touch)と flexible

Laravel 13 では、値を取り直さずに 既存キーの有効期限だけ延ばす Cache::touch が使えます(Release Notes / Cache)。

Cache::touch('key', 3600); // 成功なら true、キーが無ければ false
Cache::touch('key', now()->addHours(2));

期限切れ直後に再計算が走ると、一部のリクエストだけ遅くなることがあります。その緩和が Cache::flexible(stale-while-revalidate)です。第2引数は [新鮮とみなす秒数, 古い値を返してよい上限秒数] です。

$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});
  • 新鮮期間内: キャッシュをそのまま返す
  • 古い期間内: 古い値を返し、レスポンス後に再計算を予約する
  • 上限超過: その場で再計算する(レスポンスが遅くなる可能性がある)

アトミックロック(最小)

複数プロセスが同じ処理を同時に走らせたくないとき、Cache::lock が使えます。対応ドライバは memcached / redis / dynamodb / database / file / array などです(公式の Atomic Locks 節)。

use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('reports.generate', 10);

if ($lock->get()) {
    // 最大10秒のロックを取得
    // ... 重い処理 ...
    $lock->release();
}

クロージャを渡すと、終了時に自動で解放されます。

Cache::lock('reports.generate', 10)->get(function () {
    // 処理
});

取得を待つ場合は block を使います。制限時間内に取れなければ LockTimeoutException になります。


キャッシュタグ(概要)

関連するキーをまとめて消したいときはタグが便利です。ただし file / database / dynamodb / storage ドライバではタグは使えません。Redis など対応ストア向けの機能として押さえておくとよいです。

Cache::tags(['people', 'artists'])->put('John', $john, 60);
Cache::tags(['people', 'artists'])->flush();

既定の database ストアのままタグ例をコピーすると失敗するため、ドライバ要件を先に確認してください。


最小の実装例(コントローラ)

ダッシュボード用の集計を 60 秒キャッシュする例です。

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class DashboardController extends Controller
{
    public function index()
    {
        $stats = Cache::remember('dashboard.stats', 60, function () {
            return [
                'users' => DB::table('users')->count(),
                'orders' => DB::table('orders')->count(),
            ];
        });

        return view('dashboard', ['stats' => $stats]);
    }

    public function refresh()
    {
        Cache::forget('dashboard.stats');

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

更新系の処理のあとで関連キーを forget するか、TTL を短く保つかは、データの鮮度要件で選びます。


よくある失敗

  1. 古い記事の「既定は file」前提 — Laravel 13.x fresh の既定は database です。.env の CACHE_STORE を確認してください。
  2. database なのにテーブル未作成 — fresh 以外では make:cache-table + migrate が必要になることがあります。
  3. flush の乱用 — 共有ストア全体が消えます。キー単位の forget を基本にします。
  4. タグを database / file で使う — 非対応です。Redis 等へ切り替えるか、キー命名でグループ化します。
  5. テストで外部ストアに依存 — Feature テストでは array ドライバ(phpunit.xml で自動設定されることが多い)を使い、必要なら Cache::fake() で分離します(詳細は Laravelでテストを実装する方法)。

まとめ

  • Laravel 13.x のキャッシュは Cache ファサード(または cache())で統一操作できる
  • fresh の既定ストアは database。テーブルマイグレーションが同梱される
  • 読み取り負荷の定番は remember。TTL 延長は touch、遅延再計算は flexible
  • 同時実行の制御には Cache::lock
  • flush とタグのドライバ制約に注意する

公式の詳細(フェイルオーバー、カスタムドライバ、イベント一覧)は Cache(Laravel 13.x) を参照してください。

参考リンク

コメントする