本文へ移動
Laravel Tips

キャッシュ

時間のかかる結果を取っておいて速く返す「キャッシュ」の設定と使い方、タグ・ロック・フェイルオーバー・独自ドライバー・イベントを説明します。

キャッシュとは、一度作った結果を取っておき、次から速く返すしくみです。よく使うものを手元に置いておくメモのようなものです。アプリの仕事には、計算に時間がかかったり、取ってくるのに数秒かかったりするものがあります。その結果を少しのあいだ覚えておけば、同じデータをもう一度ほしがられたときに、すぐ返せます。取っておく場所には、Memcached や Redis のような、とても速いデータの置き場が使われることが多いです。

Laravel は、いろいろな置き場を、同じ書き方で使えるようにしてくれます。

設定#

キャッシュの設定ファイルは config/cache.php です。ここで、アプリ全体で使う標準のキャッシュ置き場(ストア)を決めます。Laravel は、Memcached・Redis・DynamoDB・リレーショナルデータベース(表でデータを持つふつうのデータベース)・ファイルシステムのディスクに、はじめから対応しています。ファイルに置く方式のドライバー(置き場ごとのつなぎ方)もあります。さらに、自動テスト用に、array と null というドライバーも用意されています。

設定ファイルには、ほかにも見直せる項目がいろいろあります。ふつうの設定では、database ドライバーが使われます。取っておくデータを、アプリのデータベースの中に、文字の形にして保存します。

ドライバーごとの準備#

データベース#

database ドライバーを使うときは、キャッシュを入れる表がデータベースに必要です。ふつうは、Laravel にはじめから入っている 0001_01_01_000001_create_cache_table.php というマイグレーション(データベースの表を作ったり変えたりする手順書)が作ってくれます。このマイグレーションが無いときは、make:cache-table という Artisan コマンドで作れます。

bash
php artisan make:cache-table

php artisan migrate

Memcached#

Memcached ドライバーを使うには、Memcached PECL パッケージ(PHP の拡張部品)を入れておく必要があります。Memcached のサーバーは、config/cache.php に並べて書けます。ここには、はじめから memcached.servers の見本が入っています。

php
'memcached' => [
    // ...
    'servers' => [
        [
            'host' => env('MEMCACHED_HOST', '127.0.0.1'),
            'port' => env('MEMCACHED_PORT', 11211),
            'weight' => 100,
        ],
    ],
],

必要なら、host には UNIX ソケット(同じパソコンの中でつなぐための道すじ)のパスも書けます。その場合は、port を 0 にします。

php
'memcached' => [
    // ...
    'servers' => [
        [
            'host' => '/var/run/memcached/memcached.sock',
            'port' => 0,
            'weight' => 100
        ],
    ],
],

Redis#

Laravel で Redis のキャッシュを使う前に、PECL で PhpRedis という PHP の拡張を入れるか、Composer で predis/predis パッケージを入れる必要があります。Laravel Sail(Docker の中で Laravel を動かす道具)には、この拡張がはじめから入っています。公式のアプリの置き場(Laravel Cloud や Laravel Forge)にも、PhpRedis の拡張がはじめから入っています。

Redis の設定のくわしいことは、公式ドキュメントの Redis のページにあります。

ストレージ#

storage ドライバーを使うと、キャッシュの値を、アプリに設定したファイルのディスクのどこにでも保存できます。S3 のディスクのような、すでにあるディスクを、キーと値のキャッシュ置き場として使いたいときに便利です。

php
'storage' => [
    'driver' => 'storage',
    'disk' => env('CACHE_STORAGE_DISK'),
    'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'),
],

DynamoDB#

DynamoDB ドライバーを使う前に、キャッシュを入れる DynamoDB の表を作る必要があります。ふつうの表の名前は cache です。ただし、cache 設定ファイルの stores.dynamodb.table の値に合わせた名前にしてください。表の名前は、DYNAMODB_CACHE_TABLE 環境変数でも決められます。

この表には、文字列のパーティションキー(行を見つける目印の列)も必要です。名前は、設定ファイルの stores.dynamodb.attributes.key の値と同じにします。ふつうは key という名前です。

DynamoDB は、ふつう、期限が切れたデータを自分から消してくれません。そのため、表で TTL(期限が来たら自動で消す機能)を有効にしてください。TTL の設定では、TTL の列の名前を expires_at にします。

次に、Laravel のアプリが DynamoDB と通信できるように、AWS SDK(AWS のサービスを PHP から使うための部品)を入れます。

bash
composer require aws/aws-sdk-php

さらに、DynamoDB のキャッシュ置き場の設定に、値を入れておいてください。AWS_ACCESS_KEY_ID や AWS_SECRET_ACCESS_KEY のような値は、ふつう、アプリの .env ファイルに書きます。

php
'dynamodb' => [
    'driver' => 'dynamodb',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
    'endpoint' => env('DYNAMODB_ENDPOINT'),
],

MongoDB#

MongoDB を使っているときは、公式の mongodb/laravel-mongodb パッケージが mongodb ドライバーを用意しています。mongodb という名前のデータベース接続で設定します。MongoDB には、期限の切れたキャッシュを自動で消せる TTL インデックスがあります。

MongoDB の設定のくわしいことは、MongoDB の公式ドキュメント(Cache and Locks)にあります。

ドライバー 説明
database アプリのデータベースの表に保存する。ふつうの設定
memcached Memcached に保存する。PECL パッケージが必要
redis Redis に保存する。PhpRedis か predis/predis が必要
dynamodb DynamoDB に保存する。表と AWS SDK が必要
file ファイルに保存する
storage 設定したファイルのディスクに保存する
mongodb MongoDB に保存する。mongodb/laravel-mongodb パッケージが必要
array その場のメモリにだけ置く。自動テスト向き
null 何も保存しない。自動テスト向き
failover 失敗したら次のストアを使う。下の「フェイルオーバー」を見る

キャッシュの使い方#

キャッシュを使う入口#

キャッシュの置き場を使うには、Cache ファサード(Cache::get() のように、クラス名と :: で機能を呼べる窓口)を使います。このページでは、ずっとこれを使います。Cache ファサードは、Laravel のキャッシュのコントラクト(約束ごと)の中身へ、短い書き方で入れる入口です。

php
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    /**
     * Show a list of all users of the application.
     */
    public function index(): array
    {
        $value = Cache::get('key');

        return [
            // ...
        ];
    }
}

複数の置き場を使い分ける#

Cache ファサードの store を使うと、いろいろな置き場を選べます。store に渡す名前は、cache 設定ファイルの stores 配列にある名前のどれかにします。

php
$value = Cache::store('file')->get('foo');

Cache::store('redis')->put('bar', 'baz', 600); // 10 分

キャッシュから取り出す#

Cache ファサードの get で、キャッシュから値を取り出します。無ければ null が返ります。2つ目の引数に、無いときに返す初期値を渡せます。

php
$value = Cache::get('key');

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

初期値には、クロージャ(名前のない関数)も渡せます。値が無いときだけクロージャが動き、その結果が返ります。データベースなどから初期値を取ってくる処理を、必要になるまで後回しにできます。

php
$value = Cache::get('key', function () {
    return DB::table(/* ... */)->get();
});

あるかどうかを調べる#

has で、値があるかを調べます。値はあっても中身が null のときは、false を返します。

php
if (Cache::has('key')) {
    // ...
}

増やす・減らす#

increment と decrement で、キャッシュの整数の値を増やしたり減らしたりできます。2つ目の引数に、増やす(減らす)量を渡せます。

php
// 値が無ければ、はじめの値を入れる
Cache::add('key', 0, now()->plus(hours: 4));

// 値を増やす・減らす
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);

取り出して、無ければ保存する#

キャッシュから取り出したいけれど、無ければ初期値を保存したい、ということがあります。たとえば、全ユーザーをキャッシュから取り、無ければデータベースから取ってキャッシュに入れる場合です。Cache::remember を使います。

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

キャッシュに無いときだけ、remember に渡したクロージャが動き、その結果がキャッシュに入ります。

値をキャッシュから取れたのか、クロージャで作ったのかを知りたいときは、rememberWithWarmth を使います。キャッシュの値と、「ウォーム」だったか(キャッシュから取れたか)の真偽値が入った配列が返ります。

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

取り出して、無ければ期限なしで保存するには rememberForever を使います。

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

古い値を返しながら作り直す(Stale While Revalidate)#

Cache::remember を使うと、キャッシュの期限が切れたとき、そのときにアクセスした人の返事が遅くなることがあります。データによっては、少し古い値を返しながら、裏で新しい値を作り直しても困りません。こうすると、値を作っているあいだに待たされる人が減ります。このやり方は「stale-while-revalidate」と呼ばれ、Cache::flexible で使えます。

flexible には、配列を渡します。1つ目の値は、キャッシュが「新鮮」な秒数、2つ目の値は、「古い」状態で返してよい秒数の終わりです。

  • 1つ目の値より前にアクセスがあったとき:作り直さずに、すぐキャッシュを返します
  • 1つ目と2つ目の値のあいだにアクセスがあったとき:古い値を返し、あとで値を作り直す処理(遅らせて動かす関数)を、返事を返した後に動くよう登録します
  • 2つ目の値のあとにアクセスがあったとき:期限切れとして、すぐ作り直します。返事は遅くなることがあります
php
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});

取り出して、消す#

キャッシュから取り出して、そのまま消したいときは pull を使います。get と同じく、無ければ null が返ります。

php
$value = Cache::pull('key');

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

キャッシュに保存する#

Cache ファサードの put で保存します。

php
Cache::put('key', 'value', $seconds = 10);

保存する秒数を渡さないと、期限なしで保存されます。

php
Cache::put('key', 'value');

秒数の整数の代わりに、期限の時刻を表す DateTime も渡せます。

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

無いときだけ保存する#

add は、キャッシュにまだ無いときだけ保存します。実際に保存できたら true、そうでなければ false を返します。add は、途中で割り込まれない処理(アトミックな操作)です。

php
Cache::add('key', 'value', $seconds);

保存の期限をのばす#

touch を使うと、すでにあるキャッシュの期限(TTL)をのばせます。キャッシュがあって、期限をのばせたら true、無ければ false を返します。

php
Cache::touch('key', 3600);

期限の時刻をはっきり決めたいときは、DateTimeInterface・DateInterval・Carbon のどれかを渡せます。

php
Cache::touch('key', now()->addHours(2));

ずっと保存する#

forever は、キャッシュをずっと保存します。期限が切れないので、消したいときは forget で手で消します。

php
Cache::forever('key', 'value');

補足

Memcached ドライバーでは、「ずっと」保存した値も、キャッシュの大きさの上限に達すると消されることがあります。

キャッシュから消す#

forget で、キャッシュの値を消せます。

php
Cache::forget('key');

期限の秒数に、0 か負の数を渡しても消えます。

php
Cache::put('key', 'value', 0);
Cache::put('key', 'value', -5);

キャッシュ全体を空にするには flush を使います。

php
Cache::flush();

キャッシュの中の、アトミックロック(下の「アトミックロック」を見る)をすべて消すには flushLocks を使います。

php
Cache::flushLocks();

注意

flush は、設定した「プレフィックス」(キーの前に付ける印)を無視して、キャッシュのすべてのデータを消します。ほかのアプリと共有しているキャッシュを空にするときは、よく考えてください。

キャッシュのメモ化#

メモ化とは、一度取った値をその場で覚えておくことです。memo ドライバーを使うと、取り出したキャッシュの値を、1回のリクエスト(またはジョブ)のあいだだけ、メモリに置いておけます。同じ処理の中で、同じ値のためにキャッシュを何度も読まなくて済み、速くなります。

メモ化したキャッシュを使うには、memo を呼びます。

php
use Illuminate\Support\Facades\Cache;

$value = Cache::memo()->get('key');

memo には、キャッシュのストアの名前も渡せます。メモ化の下で使う置き場を決めます。

php
// 標準のストアを使う
$value = Cache::memo()->get('key');

// Redis のストアを使う
$value = Cache::memo('redis')->get('key');

あるキーの最初の get は、キャッシュのストアから値を取ります。同じリクエストやジョブの中の2回目からは、メモリから取ります。

php
// キャッシュを読む
$value = Cache::memo()->get('key');

// キャッシュを読まない。メモ化された値を返す
$value = Cache::memo()->get('key');

put・increment・remember など、値を変えるメソッドを呼ぶと、メモ化された値は自動で捨てられ、変更は元のストアにまかされます。

php
Cache::memo()->put('name', 'Taylor'); // 元のキャッシュに書く
Cache::memo()->get('name');           // 元のキャッシュを読む
Cache::memo()->get('name');           // メモ化された値。キャッシュは読まない
Cache::memo()->put('name', 'Tim');    // メモ化された値を捨て、新しい値を書く
Cache::memo()->get('name');           // また元のキャッシュを読む

cache ヘルパー関数#

Cache ファサードのほかに、どこからでも呼べる cache 関数でも、キャッシュを読み書きできます。文字列を1つ渡すと、そのキーの値が返ります。

php
$value = cache('key');

キーと値の配列と、期限を渡すと、その期間だけ保存します。

php
cache(['key' => 'value'], $seconds);

cache(['key' => 'value'], now()->plus(minutes: 10));

引数なしで呼ぶと、Illuminate\Contracts\Cache\Factory の実体が返るので、ほかのキャッシュのメソッドも呼べます。

php
cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});

補足

グローバルな cache 関数の呼び出しをテストするときは、ファサードをテストするときと同じように、Cache::shouldReceive が使えます。

基本のメソッドの一覧#

メソッド 説明
get 値を取り出す。無ければ null(初期値も渡せる)
has 値があるかを調べる。中身が null なら false
increment 整数の値を増やす
decrement 整数の値を減らす
remember 取り出す。無ければクロージャの結果を保存して返す
rememberWithWarmth remember と同じ。キャッシュから取れたかの真偽値も返す
rememberForever 取り出す。無ければ期限なしで保存して返す
flexible 古い値を返しながら、裏で作り直す
pull 取り出して、消す
put 保存する。期限を渡さなければ、期限なし
add 無いときだけ保存する。保存できたら true
touch 保存してある値の期限をのばす
forever 期限なしで保存する
forget 値を消す
flush キャッシュ全体を空にする(プレフィックスを無視する)
flushLocks アトミックロックをすべて消す
memo 1回のリクエストのあいだ、値をメモリに置く

キャッシュのタグ#

注意

file・dynamodb・database・storage ドライバーでは、キャッシュのタグは使えません。

キャッシュのタグを使うと、関係のあるキャッシュに同じ印(タグ)を付けて、印ごとにまとめて消せます。タグ付きのキャッシュを使うときは、タグの名前を順に並べた配列を tags に渡します。次は、タグ付きのキャッシュに put で値を入れる例です。

タグ付きで保存する#

php
use Illuminate\Support\Facades\Cache;

Cache::tags(['people', 'artists'])->put('John', $john, $seconds);
Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);

タグ付きの値を取り出す#

タグ付きで保存した値は、そのときと同じタグを渡さないと取り出せません。保存したときと同じ順番のタグの配列を tags に渡し、取りたいキーを get に渡します。

php
$john = Cache::tags(['people', 'artists'])->get('John');

$anne = Cache::tags(['people', 'authors'])->get('Anne');

タグ付きの値を消す#

タグ(またはタグの配列)が付いた値を、まとめて消せます。次のコードは、people・authors のどちらかのタグ、または両方が付いたキャッシュをすべて消します。つまり、Anne も John も消えます。

php
Cache::tags(['people', 'authors'])->flush();

これに対して、次のコードは authors のタグが付いた値だけを消します。Anne は消えますが、John は残ります。

php
Cache::tags('authors')->flush();

アトミックロック#

注意

この機能を使うには、アプリの標準のキャッシュのドライバーを、memcached・redis・dynamodb・database・file・array のどれかにする必要があります。また、すべてのサーバーが、同じ中心のキャッシュサーバーとつながっている必要があります。

アトミックロックは、「同時に1つだけが取れる鍵」です。複数のサーバーや処理が同時に動いても、順番の取り合い(競合)を心配せずに、鍵を使えます。たとえば、Laravel Cloud は、サーバーで一度に1つの遠隔の作業だけが動くように、アトミックロックを使っています。鍵は Cache::lock で作って管理します。

ロックを使う#

php
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('foo', 10);

if ($lock->get()) {
    // 10 秒のあいだロックを取れた...

    $lock->release();
}

get にクロージャを渡すこともできます。クロージャが終わると、Laravel が自動でロックを手放します。

php
Cache::lock('foo', 10)->get(function () {
    // 10 秒のあいだロックを取れて、終わったら自動で手放す...
});

ロックを頼んだとき、すぐには取れないことがあります。そのときは、決めた秒数だけ待たせられます。待っても取れなければ、Illuminate\Contracts\Cache\LockTimeoutException が出ます。

php
use Illuminate\Contracts\Cache\LockTimeoutException;

$lock = Cache::lock('foo', 10);

try {
    $lock->block(5);

    // 最大 5 秒待って、ロックを取れた...
} catch (LockTimeoutException $e) {
    // ロックを取れなかった...
} finally {
    $lock->release();
}

block にクロージャを渡すと、上の例を短くできます。決めた秒数のあいだロックを取ろうとし、クロージャが終わると、自動で手放します。

php
Cache::lock('foo', 10)->block(5, function () {
    // 最大 5 秒待って、10 秒のあいだロックを取れた...
});

別のプロセスでロックを手放す#

ロックをあるプロセス(動いているプログラム)で取り、別のプロセスで手放したいことがあります。たとえば、Web のリクエストでロックを取り、そのリクエストが動かしたキューのジョブの終わりで手放す場合です。このときは、ロックの「持ち主のトークン」をジョブに渡します。ジョブは、そのトークンで、ロックを作り直します。

次の例では、ロックを取れたときだけ、キューのジョブを動かします。ロックの owner メソッドで、持ち主のトークンをジョブに渡します。

php
$podcast = Podcast::find($id);

$lock = Cache::lock('processing', 120);

if ($lock->get()) {
    ProcessPodcast::dispatch($podcast, $lock->owner());
}

アプリの ProcessPodcast ジョブの中では、持ち主のトークンを使って、ロックを元に戻して手放せます。

php
Cache::restoreLock('processing', $this->owner)->release();

いまの持ち主を気にせずに、ロックを手放したいときは forceRelease を使います。

php
Cache::lock('processing')->forceRelease();

ロックの期限をのばす#

いま持っているロックの期限をのばしたいときは、refresh を使います。秒数を渡さなければ、ロックのはじめの長さが使われます。長くかかる仕事で、とても長い期限のロックを取る代わりに、短いロックを取って、ときどきのばしたいときに便利です。

php
$lock = Cache::lock('generate-reports', 60);

if ($lock->get()) {
    foreach ($reports as $report) {
        $report->generate();

        // ロックをさらに 60 秒のばす
        $lock->refresh();
    }

    $lock->release();
}

同時に動く数を制限する#

アトミックロックの機能には、クロージャが同時に動く数を制限する方法もあります。システム全体で、同時に1つしか動かしたくないときは withoutOverlapping を使います。

php
Cache::withoutOverlapping('foo', function () {
    // 最大 10 秒待って、ロックを取れた...
});

ふつうは、クロージャが終わるまでロックを持ち、ロックを取るために最大 10 秒待ちます。これらの値は、引数で変えられます。

php
Cache::withoutOverlapping('foo', function () {
    // 最大 5 秒待って、120 秒のあいだロックを取れた...
}, lockFor: 120, waitFor: 5);

待つ時間のうちにロックを取れなかったときは、Illuminate\Contracts\Cache\LockTimeoutException が出ます。

同時に動く数の上限を決めたいときは、funnel を使います。funnel は、ロックに対応したキャッシュのドライバーなら使えます。

php
Cache::funnel('foo')
    ->limit(3)
    ->releaseAfter(60)
    ->block(10)
    ->then(function () {
        // 同時実行のロックを取れた...
    }, function () {
        // 同時実行のロックを取れなかった...
    });

funnel のキーは、制限したいもの(資源)の名前です。limit は同時に動ける最大の数、releaseAfter は、取った枠を自動で手放すまでの安全のための秒数、block は、空いた枠を待つ秒数です。

失敗のときのクロージャを渡さず、例外で受けたいときは、2つ目のクロージャを省きます。待つ時間のうちに枠を取れないと、Illuminate\Cache\Limiters\LimiterTimeoutException が出ます。

php
use Illuminate\Cache\Limiters\LimiterTimeoutException;

try {
    Cache::funnel('foo')
        ->limit(3)
        ->releaseAfter(60)
        ->block(10)
        ->then(function () {
            // 同時実行のロックを取れた...
        });
} catch (LimiterTimeoutException $e) {
    // 同時実行のロックを取れなかった...
}

決まったキャッシュのストアを使いたいときは、そのストアの funnel を呼びます。

php
Cache::store('redis')->funnel('foo')
    ->limit(3)
    ->block(10)
    ->then(function () {
        // "redis" ストアで同時実行のロックを取れた...
    });

補足

funnel を使うには、キャッシュのストアが Illuminate\Contracts\Cache\LockProvider インターフェイスを持っている必要があります。ロックに対応していないストアで使うと、BadMethodCallException が出ます。

メソッド 説明
Cache::lock ロックを作る。名前と秒数を渡す
get ロックを取る。取れたら true
block 決めた秒数まで待って、ロックを取る。取れなければ例外
release ロックを手放す
owner ロックの持ち主のトークンを返す
Cache::restoreLock トークンを使って、ロックを作り直す
forceRelease 持ち主を気にせずに、ロックを手放す
refresh いま持っているロックの期限をのばす
withoutOverlapping システム全体で、同時に1つだけクロージャを動かす
funnel クロージャが同時に動く数を制限する

キャッシュのフェイルオーバー#

failover ドライバーは、キャッシュの置き場が使えなくなったとき、自動でほかの置き場に切りかえてくれます。いちばん手前の置き場が、何かの理由で失敗したら、Laravel は、設定した一覧の次の置き場を自動で試します。キャッシュが止まると困る本番の環境で、止まらない(可用性を保つ)ようにするのに役立ちます。

failover の置き場は、ドライバーに failover を指定し、順に試す置き場の名前の配列を書いて作ります。Laravel の config/cache.php には、見本が入っています。

php
'failover' => [
    'driver' => 'failover',
    'stores' => [
        'database',
        'array',
    ],
],

failover ドライバーの置き場を作ったら、アプリの .env ファイルで、標準のキャッシュの置き場にそれを指定します。そうしないと、フェイルオーバーは働きません。

ini
CACHE_STORE=failover

キャッシュの操作が失敗して、フェイルオーバーが働くと、Laravel は Illuminate\Cache\Events\CacheFailedOver イベントを出します。これを受け取れば、キャッシュの置き場の失敗を、記録したり知らせたりできます。

独自のキャッシュドライバーを作る#

ドライバーを書く#

独自のキャッシュドライバーを作るには、まず Illuminate\Contracts\Cache\Store コントラクト(守るべき約束ごと)を実装します(約束ごとにあるメソッドを、自分のクラスに全部書くことです)。MongoDB のキャッシュなら、次のような形になります。

php
<?php

namespace App\Extensions;

use Illuminate\Contracts\Cache\Store;

class MongoStore implements Store
{
    public function get($key) {}
    public function many(array $keys) {}
    public function put($key, $value, $seconds) {}
    public function putMany(array $values, $seconds) {}
    public function increment($key, $value = 1) {}
    public function decrement($key, $value = 1) {}
    public function forever($key, $value) {}
    public function touch($key, $seconds) {}
    public function forget($key) {}
    public function flush() {}
    public function getPrefix() {}
}

あとは、これらのメソッドを、MongoDB の接続を使って実装するだけです。実装の見本は、Laravel のフレームワークのソースコードにある Illuminate\Cache\MemcachedStore を見てください。実装が終わったら、Cache ファサードの extend で、ドライバーの登録を済ませます。

php
Cache::extend('mongo', function (Application $app) {
    return Cache::repository(new MongoStore);
});

補足

独自のドライバーのコードを置く場所に迷ったら、app フォルダの中に Extensions という名前空間(クラスの住所のような名前)を作る方法があります。ただし、Laravel は、アプリのフォルダの構成を固く決めていません。好きなように整えてかまいません。

メソッド 説明
get 値を取り出す
many 複数のキーの値をまとめて取り出す
put 値を保存する
putMany 複数の値をまとめて保存する
increment 値を増やす
decrement 値を減らす
forever 期限なしで保存する
touch 期限をのばす
forget 値を消す
flush すべて消す
getPrefix キーの前に付ける印(プレフィックス)を返す

ドライバーを登録する#

独自のドライバーを Laravel に登録するには、Cache ファサードの extend を使います。登録は、booting コールバック(あとで呼んでもらうために渡しておく関数)の中でします。なぜなら、ほかのサービスプロバイダ(アプリの起動のときに、道具を登録する場所)が、自分の boot メソッドの中でキャッシュを読むことがあるからです。booting を使うと、すべてのサービスプロバイダの register が呼ばれたあと、boot が呼ばれる直前に登録できます。booting コールバックは、App\Providers\AppServiceProvider の register メソッドに書きます。

php
<?php

namespace App\Providers;

use App\Extensions\MongoStore;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        $this->app->booting(function () {
             Cache::extend('mongo', function (Application $app) {
                 return Cache::repository(new MongoStore);
             });
         });
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // ...
    }
}

extend の1つ目の引数は、ドライバーの名前です。config/cache.php の driver に書く名前と同じにします。2つ目の引数は、Illuminate\Cache\Repository を返すクロージャです。クロージャには、$app(サービスコンテナの実体)が渡されます。

登録が済んだら、CACHE_STORE 環境変数、または config/cache.php の default を、作ったドライバーの名前に変えます。

キャッシュのイベント#

キャッシュの操作のたびに動かしたいコードがあるときは、キャッシュが出すイベント(「〜が起きた」という知らせ)を受け取ります。イベントは、すべて Illuminate\Cache\Events の中にあります。

イベント 説明
CacheHit キャッシュに値があった
CacheMissed キャッシュに値が無かった
RetrievingKey 値を1つ取り出そうとしている
RetrievingManyKeys 複数の値を取り出そうとしている
WritingKey 値を1つ書こうとしている
WritingManyKeys 複数の値を書こうとしている
KeyWritten 値を書けた
KeyWriteFailed 値を書けなかった
ForgettingKey 値を消そうとしている
KeyForgotten 値を消せた
KeyForgetFailed 値を消せなかった
CacheFlushing キャッシュ全体を空にしようとしている
CacheFlushed キャッシュ全体を空にできた
CacheFlushFailed キャッシュ全体を空にできなかった
CacheLocksFlushing ロックをすべて消そうとしている
CacheLocksFlushed ロックをすべて消せた
CacheLocksFlushFailed ロックをすべて消せなかった

速さを上げたいときは、キャッシュのイベントを止められます。config/cache.php の、そのストアの設定で、events を false にします。

php
'database' => [
    'driver' => 'database',
    // ...
    'events' => false,
],

関連するページ#

公式ドキュメント(英語)

2026年10月5日時点の内容をもとに、日本語でまとめています。

ページの一覧