Chapter sebelumnya membuktikan cache menyimpan salinan hasil komputasi di tabel cache aplikasi kita. Sebenarnya tabel database hanyalah salah satu tempat untuk menyimpan entri cache — Laravel menyebut mekanisme penyimpanan tersebut cache driver, dan pilihannya ada tujuh. Pilihan driver menentukan kecepatan baca-tulis, ketahanan data terhadap restart server, dan bagaimana aplikasi menskalakan saat berjalan di beberapa server. Pada chapter ini kita bedah setiap driver, cara berganti di antara mereka, dan bagaimana satu aplikasi bisa memakai beberapa store sekaligus.

CACHE_STORE sebagai Pintu Depan #

Sama seperti session, driver cache dipilih lewat satu variabel lingkungan. File .env hasil laravel new langsung berisi baris berikut, karena sejak Laravel 11 nilai default-nya adalah database:

dotenv
CACHE_STORE=database

Nilai tersebut dibaca config/cache.php sebagai store bawaan yang dipakai Cache facade setiap kali kita tidak menyebut store secara eksplisit. Perintah artisan berikut membuktikan store database aktif di sandbox dan bisa dikosongkan sepenuhnya:

text
$ php artisan cache:clear

   INFO  Application cache cleared successfully.

Peta Tujuh Driver Cache #

Berikut perbandingan driver yang tersedia di config/cache.php beserta karakternya:

DriverLokasi dataKecepatanCatatan penting
databaseTabel cacheSedangDefault Laravel 11+; butuh migrasi bawaan
filestorage/framework/cache/dataCepat (disk lokal)Tanpa infrastruktur tambahan; satu server saja
redisMemori server RedisSangat cepatStandar trafik besar; butuh server Redis
memcachedMemoriSangat cepatAlternatif Redis; butuh server Memcached
dynamodbLayanan AWSCepatButuh kredensial AWS
apcMemori proses PHP (APCu)Sangat cepatTerbatas per server; tidak di config bawaan
arrayMemori proses PHP-Khusus testing; mati saat request selesai

Perbedaan paling menentukan bukan kecepatan, melainkan berbagi: file dan apc hanya terlihat oleh satu server, sedangkan redis/memcached/dynamodb bisa dibaca semua server aplikasi. Ketika load balancer membagi request Budi ke server A dan Siti ke server B, hanya driver berbagi yang membuat entri cache tetap konsisten.

Satu Aplikasi, Banyak Store #

Kita tidak terikat pada satu pilihan. Method Cache::store() mengambil repository untuk store bernama, dan tiap store punya ruang kunci terpisah. Runner berikut menulis kunci yang sama ke tiga store berbeda:

php
use Illuminate\Support\Facades\Cache;

Cache::store('array')->put('kota', 'di array', 300);
Cache::store('database')->put('kota', 'di database', 300);

echo 'array=' . Cache::store('array')->get('kota')
    . ' database=' . Cache::store('database')->get('kota') . "\n";
echo 'default=' . Cache::get('kota') . "\n";

Output yang Dihasilkan:

text
array=di array database=di database
default=di database

Baris terakhir membuktikan facade memakai store default dari CACHE_STORE, yaitu database. Driver file juga bisa diuji tanpa konfigurasi apa pun — entri ditulis sebagai file berisi timestamp kedaluwarsa dan nilai ter-serialize:

php
use Illuminate\Support\Facades\Cache;

$file = Cache::store('file');
$file->forget('kota');
$file->put('kota', 'Bandung', 300);

$path = storage_path('framework/cache/data');
$files = array_values(array_filter(
    iterator_to_array(new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS))),
    fn ($f) => $f->getExtension() !== 'gitignore',
));

echo 'jumlah_file=' . count($files)
    . ' isi_file=' . substr(file_get_contents($files[0]->getPathname()), 0, 26) . "\n";
echo 'get_dari_file=' . $file->get('kota') . "\n";

Output yang Dihasilkan:

text
jumlah_file=1 isi_file=1767261900s:7:"Bandung";
get_dari_file=Bandung

Satu kunci menghasilkan tepat satu file di dalam direktori storage/framework/cache/data (nama file adalah hash dari kunci). Isi file adalah gabungan timestamp kedaluwarsa 1767261900 dan nilai s:7:"Bandung"; yang ter-serialize — format yang persis sama dengan kolom tabel cache dari chapter sebelumnya.

Driver array untuk Testing #

phpunit.xml bawaan Laravel sudah menyetel CACHE_STORE=array khusus lingkungan testing. Ada dua alasan kuat untuk itu. Pertama, tanpa disk maupun query database, test berjalan lebih cepat. Kedua, memori proses mati bersama tiap test sehingga entri cache dari test yang satu tidak pernah bocor ke test berikutnya.

Migrasi Tabel cache #

Driver database menyimpan entri di tabel cache yang dibuat migrasi bawaan 0001_01_01_000001_create_cache_table.php:

php
Schema::create('cache', function (Blueprint $table) {
    $table->string('key')->primary();
    $table->mediumText('value');
    $table->bigInteger('expiration')->index();
});

Perintah cache:table tersedia untuk aplikasi lama yang belum memilikinya. Di sandbox kita perintah ini menolak dengan jujur karena migrasinya sudah ada sejak awal:

text
$ php artisan cache:table

   ERROR  Migration already exists.

Selain tabel cache, migrasi yang sama juga membuat cache_locks — tabel khusus untuk atomic lock, bahasan lanjutan di luar section ini.

Redis: phpredis atau predis #

Driver redis berbicara ke server Redis lewat salah satu dari dua jalan: ekstensi phpredis pada binary PHP (yang terpasang di sandbox ini), atau package predis/predis yang murni PHP melalui composer require predis/predis. Keduanya setara secara fitur; phpredis sedikit lebih cepat karena ditulis sebagai ekstensi C. Sandbox kita tidak menjalankan server Redis, sehingga operasi nyata gagal dengan pesan yang jujur:

text
RedisException: Connection refused

Setelah server Redis tersedia, koneksi diatur di config/database.php bagian redis, lalu store cache memakainya lewat REDIS_CACHE_CONNECTION di .env.

Prefix untuk Kunci #

Ketika beberapa aplikasi berbagi satu server Redis atau satu database, kunci kota dari aplikasi A bisa bertabrakan dengan kunci kota dari aplikasi B. CACHE_PREFIX mencegahnya — Laravel menyusun prefix dari nama aplikasi:

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

DB::table('cache')->delete();
Cache::put('kota', 'prefix bawaan', 300);

config(['cache.prefix' => 'myapp-cache-']);
Cache::forgetDriver(); // paksa manager membangun ulang store

Cache::put('kota', 'prefix baru', 300);
echo 'kunci_db=' . json_encode(
    DB::table('cache')->where('key', 'like', '%kota')->pluck('key')->all()
) . "\n";

Output yang Dihasilkan:

text
kunci_db=["laravel-cache-kota","myapp-cache-kota"]

Dua entri kini hidup berdampingan di tabel yang sama dengan prefix berbeda — laravel-cache- berasal dari APP_NAME=Laravel. Panggilan forgetDriver() diperlukan karena cache manager menyimpan store yang sudah dibangun; tanpa panggilan tersebut, perubahan cache.prefix di tengah proses tidak akan terbaca. Daftar lengkap opsi konfigurasinya tersedia di dokumentasi Cache .

Dengan driver dipilih, kita tinggal mengoperasikan cache-nya: menyimpan, membaca, menghapus, dan pola remember() yang merangkai semuanya — materi chapter Cache Operations .