Laravel Cacheの使い方完全ガイド|Cacheファサードの基本・設定・タグ・最新機能まで

実装・応用テクニック
  • カテゴリ: キャッシュ管理
  • 掲載バージョン: Laravel 13・PHP 8.3(php artisan tinkerで全コード例を実行確認済み)
  • 対象: Cacheファサード / config/cache.php / キャッシュタグ / キャッシュ関連Artisanコマンド
  • 関連: cache()ヘルパー / config:cache / optimize
  • 変更履歴: Laravel 11以降、既定のキャッシュストアは「file」から「database」に変更

要点(TL;DR)

  • Laravel CacheはDBアクセスや重い計算の結果を一時保存し、アプリを高速化する仕組み。Cacheファサードで操作する
  • 既定ストアはLaravel 11以降「database」(それ以前は「file」)。.envCACHE_STOREで切り替える
  • 基本はCache::remember('key', $ttl, fn () => 重い処理())の1パターンを覚えれば大半のケースに対応できる
  • キャッシュタグ(tags())はfile・databaseドライバでは非対応。array・redis・memcachedのみ利用可
  • 罠:has()missing()は値がnullのとき「存在しない」と判定する(実機検証で確認)

Laravelのキャッシュとは?

キャッシュとは、一度取得・計算した結果を一時的に保存しておき、次回以降は同じ処理をやり直さずに済ませる仕組みです。LaravelにはCacheコンポーネントが標準搭載されており、DBクエリの結果、外部APIのレスポンス、集計処理の結果などをCacheファサード経由で簡単にキャッシュできます。

効果が大きいのは「アクセス頻度が高いのに、元データの更新頻度は低い」データです。例えばダッシュボードの集計値、カテゴリ一覧、設定値などはキャッシュとの相性が良く、DBの負荷とレスポンスタイムを同時に改善できます。逆に、リクエストごとに変わる値(ログインユーザー固有のデータなど)をキャッシュすると、古いデータを表示し続けるバグの原因になるため注意が必要です。

対応キャッシュドライバーと選び方

Laravelは複数のキャッシュストアをconfig/cache.phpで定義でき、.envCACHE_STOREで既定ストアを切り替えます。主なドライバーの特徴は次のとおりです(キャッシュタグ対応可否は実機で例外発生の有無を確認済み)。

ドライバー永続化複数サーバー間共有タグ対応主な用途
fileあり不可非対応単一サーバーでの手軽な利用(Laravel 10以前の既定)
databaseあり可(共有DB利用時)非対応追加ミドルウェア不要。Laravel 11以降の既定
redis設定次第対応本番環境の第一候補。高速・タグ・アトミックロック対応
memcachedなし(揮発性)対応純粋なキャッシュ専用途で高速
arrayなし(リクエスト内のみ)不可対応テスト・ローカル検証用
dynamodbあり非対応AWS環境でのマネージド運用

実機検証メモ:新規に composer create-project laravel/laravel で作成したLaravel 13プロジェクトでは、.envの初期値が CACHE_STORE=database でした。以前の記事や書籍で「既定はfileドライバー」と書かれている場合、Laravel 10以前の情報である可能性が高いので注意してください。

設定方法(.env / config/cache.php)

# .env
CACHE_STORE=redis
CACHE_PREFIX=myapp_cache

REDIS_HOST=127.0.0.1
REDIS_PORT=6379

databaseドライバーを使う場合、Laravel 11以降はデフォルトのマイグレーションにcacheテーブルが最初から含まれているため追加作業は不要です。Laravel 10以前のプロジェクトを移行する場合のみ、テーブル作成コマンド(make:cache-table)が必要になります。設定値そのものをキャッシュして起動を高速化したい場合はconfig:cacheコマンドを使います。

Cacheファサードの基本操作(実機検証コード)

以下はすべて php artisan tinker 上で実際に実行し、出力を確認済みのコードです。

保存・取得・削除

use Illuminate\Support\Facades\Cache;

// 保存(TTLは秒)
Cache::put('key', 'value', 60);

// 取得(なければ第2引数を返す)
Cache::get('key');                 // "value"
Cache::get('missing', 'default');  // "default"

// 削除
Cache::forget('key');
Cache::get('key');                 // null

remember系:重い処理を「一度だけ」実行してキャッシュする

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

// 1回目:クロージャが実行されキャッシュに保存される
$count = Cache::remember('users:count', 60, function () {
    return DB::table('users')->count();
});

// 2回目(TTL内):クロージャは実行されず、キャッシュ値がそのまま返る
$count = Cache::remember('users:count', 60, function () {
    return DB::table('users')->count();
});

実際にtinkerでクロージャ内に echo を仕込んで検証したところ、2回目の呼び出しではクロージャが実行されないことを確認しています。有効期限を設けず永続化したい場合は Cache::rememberForever()(別名 sear())を使います。

加算・排他的な追加・取り出し

use Illuminate\Support\Facades\Cache;

// add: キーが存在しない場合のみ保存(存在すればfalseを返す)
Cache::add('cnt', 1, 60);   // true
Cache::add('cnt', 99, 60);  // false(既に存在するため保存されない)

// increment / decrement
Cache::increment('cnt');       // 2
Cache::increment('cnt', 5);    // 7
Cache::decrement('cnt', 2);    // 5

// pull: 取得と同時に削除
$value = Cache::pull('cnt');   // 5
Cache::has('cnt');             // false

主要メソッド早見表

メソッド用途
get($key, $default)取得。無ければ$default
put($key, $value, $ttl)保存(秒指定)
add($key, $value, $ttl)未存在時のみ保存
remember($key, $ttl, $callback)あれば取得、無ければ実行して保存
rememberForever($key, $callback)期限なしでremember
increment() / decrement()数値の加減算
pull($key, $default)取得と同時に削除
forget($key) / flush()個別削除 / 全削除
has($key) / missing($key)存在確認(null値は「なし」扱い)

cache()ヘルパーとの違い

グローバル関数のcache()ヘルパーはCacheファサードの薄いラッパーで、機能的にはほぼ同等です。1行で完結させたい場合はcache()、IDEの型補完や可読性を重視するチーム開発ではCacheファサードが向いています。cache()ヘルパー固有の引数オーバーロード(文字列キーで取得・配列で保存)や落とし穴の詳細はcache()ヘルパーの記事で解説しています。

キャッシュタグ(tags)の使い方と罠

キャッシュタグを使うと、複数のキャッシュ項目をグループ化して一括削除できます。ただし前述のとおりfile・databaseドライバーは非対応です。

use Illuminate\Support\Facades\Cache;

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

// "people"タグが付いたキャッシュだけを一括削除
Cache::tags('people')->flush();

実機検証で判明した見落としやすい注意点があります。tags()に渡すタグは、同じ組み合わせ・同じ並び順でないと同一のキャッシュとして扱われません。

Cache::tags(['a', 'b'])->put('k1', 'v1', 30);

Cache::tags(['a', 'b'])->get('k1'); // "v1"(同じ並び順なのでヒット)
Cache::tags(['b', 'a'])->get('k1'); // null(並び順が違うためヒットしない)

tinkerでtags(['a','b'])tags(['b','a'])を実際に試したところ、後者はnullを返しました。タグは配列ではなく「タグの並びそのもの」で名前空間を作る実装になっているため、タグを使う際はプロジェクト内で並び順を統一するルールを決めておくと事故を防げます。

Laravel最新版で使えるキャッシュ機能(flexible / withoutOverlapping)

Laravel 11以降で追加された、比較的新しい2つのメソッドも実機で動作確認しました。

flexible():期限切れ直前は古い値を返しつつ裏で再計算(stale-while-revalidate)

use Illuminate\Support\Facades\Cache;

// [新鮮とみなす秒数, 完全に破棄するまでの秒数] を指定
$stats = Cache::flexible('stats', [5, 30], function () {
    return now()->timestamp; // 重い集計処理を想定
});

remember()との違いは、期限が近づいたときにリクエストを待たせず「今ある値をとりあえず返し、裏側で再計算する」点です。ダッシュボード集計のように多少の遅延が許容できるが、レスポンス遅延は避けたいケースに向いています。

withoutOverlapping():同じ処理の同時多重実行を防ぐ

use Illuminate\Support\Facades\Cache;

Cache::withoutOverlapping('report:monthly', function () {
    // 集計などの重い処理。同じキーでの同時実行はロックで待機・排他される
    return generateMonthlyReport();
}, lockFor: 5, waitFor: 3);

内部的にはキャッシュストアのアトミックロック(Store::lock())を利用しており、同じ集計処理をリクエストが重なって二重実行してしまう問題を防げます。ロック機能はdatabaseredismemcachedarrayドライバーで利用できます。

キャッシュ関連のArtisanコマンドまとめ

デプロイ時によく使うキャッシュ系コマンドです。個別の詳細やオプションは各記事、コマンド全体の一覧はArtisanコマンド徹底解説を参照してください。

コマンド内容
php artisan cache:clearアプリケーションキャッシュ(Cacheで保存した値)を全削除
config:cache設定ファイルを1つにまとめてキャッシュし起動を高速化
config:clear設定キャッシュを削除
view:cacheBladeビューを事前コンパイルしてキャッシュ
view:clearコンパイル済みビューを削除
route:clearルートキャッシュを削除
optimizeconfig・route・view等のキャッシュを一括作成
optimize:clear上記のキャッシュを一括削除
make:cache-tabledatabaseドライバー用のcacheテーブルのマイグレーションを生成

注意cache:clearが消すのはCacheファサードで保存したアプリケーションキャッシュのみです。設定ファイルのキャッシュ(config:cache)やビューのキャッシュ(view:cache)は別物なので、デプロイ後に古い設定が反映されないときはconfig:clearoptimize:clearまで実行できているか確認してください。

Laravelにページキャッシュ(フルページキャッシュ)はある?

Laravel自体には、レスポンス全体を丸ごと保存する「フルページキャッシュ」機能は標準搭載されていません。代わりに用意されているのは、ブラウザやCDNにキャッシュを許可するHTTPヘッダーを付与するcache.headersミドルウェアです。

use Illuminate\Http\Middleware\SetCacheHeaders;

Route::get('/profile', function () {
    // ...
})->middleware('cache.headers:public;max_age=2628000;etag');

// 配列で指定することも可能(SetCacheHeaders::using()が文字列に変換)
Route::get('/profile', function () {
    // ...
})->middleware(SetCacheHeaders::using(['public' => true, 'max_age' => 2628000, 'etag' => true]));

このミドルウェアはあくまでCache-ControlETagLast-Modifiedといったレスポンスヘッダーを付けるだけで、Laravel自身がサーバー内でレスポンスを保存するわけではありません。サーバーサイドでレスポンス全体をキャッシュしたい場合は、spatie/laravel-responsecacheのようなパッケージを追加するか、Nginx・Varnish・CloudflareなどのCDN/リバースプロキシ層でキャッシュするのが一般的です。

よくある質問

Laravelのconfig/cacheはどこにありますか?

キャッシュストアの設定ファイルはプロジェクトルートのconfig/cache.phpです。既定ストアの指定は同ファイル内の'default' => env('CACHE_STORE', 'database')で行われ、実際の値は.envCACHE_STOREで上書きします。

Laravelでキャッシュをクリアするコマンドは?

アプリケーションキャッシュはphp artisan cache:clearです。設定・ルート・ビューなどのキャッシュも含めてまとめて削除したい場合はphp artisan optimize:clearを使うと一括で削除できます。

Laravelのページキャッシュとは?

Laravel自体にフルページキャッシュ機能はなく、一般的には「cache.headersミドルウェアでブラウザ・CDNキャッシュを許可する」「spatie/laravel-responsecache等のパッケージでレスポンスを保存する」のいずれかを指します。詳細は本記事の「ページキャッシュ」セクションを参照してください。

トラブルシュート(エラー別)

症状/エラー原因対処
BadMethodCallException: This cache store does not support tagging.filedatabaseドライバーでtags()を使用redismemcachedarrayドライバーに切り替える
タグを指定したのにget()nullを返すtags()に渡した配列の並び順が保存時と異なる保存時と同じ順序でタグを指定する
デプロイ後も設定変更が反映されないconfig:cache実行後、設定キャッシュが古いままphp artisan config:clearまたはoptimize:clearを実行
Redis connection refusedRedisサーバー未起動・接続情報の誤りconfig/database.phpのredis設定と起動状態を確認
キャッシュしたはずの値が消えているTTLを0や負数で保存した正の秒数を指定するか、rememberForever()を使う

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント