Semua ini kita lakukan dengan kunci tunggal: simpan posts:semua, lupa menghapusnya, dan daftar basi bertahan sampai TTL habis. Semakin banyak kunci, semakin rapuh daftar Cache::forget() manual itu — satu saja terlewat, satu sudut aplikasi menampilkan data lama. Cache tags mengatasinya dengan cara berpikir berbeda: alih-alih menghapus per kunci, kita memberi label kelompok pada entri, lalu menghapus seluruh kelompok sekaligus. Pada chapter ini kita pelajari sintaksnya, driver mana saja yang mendukungnya, dan jalan keluar portable bila driver kita tidak termasuk.
Konsep dan Sintaks #
Method Cache::tags() menerima satu label atau array label, lalu mengembalikan repository bertag yang API-nya sama seperti biasa:
use Illuminate\Support\Facades\Cache;
Cache::tags(['produk'])->put('produk:1', 'Laptop', 300);
Cache::tags(['produk', 'unggulan'])->remember('produk:daftar', 300, function () {
return ['Laptop', 'Mouse'];
});
Cache::tags('produk')->flush(); // hapus SEMUA entri bertag produk
Satu entri boleh membawa beberapa tag sekaligus — entri produk:daftar di atas teriak “saya produk” sekaligus “saya unggulan”, sehingga flush dari kedua sisi mana pun ikut menghapusnya. Tag berperan sebagai namespace untuk kelompok invalidasi: ketika tabel products berubah, kita cukup membuang satu kelompok tanpa perlu tahu kunci apa saja yang pernah ditulisnya.
Dukungan Driver yang Tidak Merata #
Fitur tags ternyata tidak tersedia di semua driver. Mencobanya di store database langsung dijawab exception:
use Illuminate\Support\Facades\Cache;
try {
Cache::tags(['produk'])->put('produk:1', 'Laptop', 300);
} catch (Throwable $e) {
echo get_class($e) . ': ' . $e->getMessage() . "\n";
}Output yang Dihasilkan:
BadMethodCallException: This cache store does not support tagging.Alasannya struktural: menghapus “semua kunci bertag X” menuntut store yang bisa melacak keanggotaan tag secara efisien, dan itu hanya dimungkinkan oleh driver memori tertentu. Kita bisa mengecek dukungannya lewat Cache::store($nama)->supportsTags(). Hasil verifikasi pada Laravel 13.36 di sandbox kita:
| Driver | Mendukung tags | Sumber verifikasi |
|---|---|---|
array | Ya | Diuji langsung |
database | Tidak | Diuji langsung (error di atas) |
file | Tidak | Diuji langsung |
failover | Ya | Diuji langsung |
redis | Ya | Diuji via supportsTags() (tanpa server) |
memcached | Ya | Mewarisi TaggableStore; butuh ekstensi Memcached |
apc | Ya | Mewarisi TaggableStore |
dynamodb | Tidak | Kelas DynamoDbStore tanpa method tags() di versi ini |
Lima baris teratas diuji langsung lewat supportsTags() di sandbox — store redis ikut teruji karena membangun store-nya tidak membutuhkan koneksi ke server. Tiga sisanya diverifikasi dari kode framework karena membutuhkan ekstensi yang tidak terpasang.
Demo Tanpa Redis: Driver array #
Karena array termasuk store yang mendukung tags, kita bisa merasakan perilakunya tanpa server Redis. Perhatikan pula satu kejutan penting di output berikut:
use Illuminate\Support\Facades\Cache;
$tagged = Cache::store('array')->tags(['produk', 'unggulan']);
$tagged->put('produk:1', 'Laptop', 300);
Cache::store('array')->put('lainnya', 'di luar tag', 300);
$viaTag = $tagged->get('produk:1');
$tanpaTag = Cache::store('array')->get('produk:1');
Cache::store('array')->tags('produk')->flush();
echo 'sebelum_flush: via_tag=' . $viaTag
. ' tanpa_tag=' . var_export($tanpaTag, true) . "\n";
echo 'setelah_flush: produk:1='
. var_export(Cache::store('array')->get('produk:1'), true)
. ' lainnya=' . Cache::store('array')->get('lainnya') . "\n";Output yang Dihasilkan:
sebelum_flush: via_tag=Laptop tanpa_tag=NULL
setelah_flush: produk:1=NULL lainnya=di luar tagDua fakta terbaca jelas. Pertama, flush() pada tag produk hanya memusnahkan entri bertag — kunci lainnya selamat. Kedua, tanpa_tag=NULL membuktikan entri bertag disimpan di dalam namespace tag, sehingga pembacaan tanpa tag yang sama tidak menemukannya. Konsekuensinya: tulis dan baca harus lewat tag yang sama.
$panggilan = 0;
$hitung = function () use (&$panggilan) {
$panggilan++;
return 'daftar produk: 2 item';
};
$t = Cache::store('array')->tags('produk');
$t->forget('produk:daftar');
$t->remember('produk:daftar', 300, $hitung);
$t->remember('produk:daftar', 300, $hitung);
$t->flush();
$t->remember('produk:daftar', 300, $hitung);
echo "remember: panggilan_closure={$panggilan}\n";Output yang Dihasilkan:
remember: panggilan_closure=2Closure dieksekusi sekali untuk dua panggilan beruntun, lalu sekali lagi setelah flush() — siklus hit-miss-invalidasi bekerja dalam kelompok.
Workaround Portable untuk Driver file dan database #
Bagaimana bila production kita memakai database atau file? Kita bisa meniru tags dengan satu kunci indeks yang mencatat daftar kunci per kelompok:
use Illuminate\Support\Facades\Cache;
function taggedPut(string $tag, string $key, mixed $value, int $ttl): void
{
$index = Cache::get("tagindeks:{$tag}", []);
$index[$key] = true;
Cache::forever("tagindeks:{$tag}", $index);
Cache::put($key, $value, $ttl);
}
function taggedFlush(string $tag): int
{
$index = Cache::get("tagindeks:{$tag}", []);
foreach (array_keys($index) as $key) {
Cache::forget($key);
}
Cache::forget("tagindeks:{$tag}");
return count($index);
}Pengujian di store database membuktikan tiga entri bertag musnah sementara kunci luar tetap utuh:
Cache::flush();
taggedPut('produk', 'produk:1', 'Laptop', 300);
taggedPut('produk', 'produk:2', 'Mouse', 300);
taggedPut('produk', 'produk:3', 'Monitor', 300);
Cache::put('statistik:jumlah-user', 3, 300);
$hapus = taggedFlush('produk');
echo 'taggedFlush: kunci_dihapus=' . $hapus
. ' produk:1=' . var_export(Cache::get('produk:1'), true)
. ' statistik:jumlah-user=' . Cache::get('statistik:jumlah-user') . "\n";
echo 'indeks_juga_hilang=' . var_export(Cache::get('tagindeks:produk'), true) . "\n";Output yang Dihasilkan:
taggedFlush: kunci_dihapus=3 produk:1=NULL statistik:jumlah-user=3
indeks_juga_hilang=NULLPola Penamaan Tag #
Konvensi penamaan menentukan seberapa presisi invalidasi kita. Pola yang umum: satu tag per entity (post:1, post:2) dan satu tag per kelompok daftar (posts). Entri daftar milik pengguna tertentu membawa dua tag sekaligus — posts dan user:7 — sehingga perubahan postingan maupun perubahan data pengguna sama-sama memicu refresh. Prinsipnya sederhana: setiap alasan entri itu basi menjadi satu tag.
Tag memberi kita kelompok penghapusan; satu pertanyaan terakhir tinggal dijawab: kapan sebaiknya penghapusan itu dipicu secara otomatis saat data berubah? Jawabannya — dari observer model sampai pola write-through — adalah inti chapter Cache Invalidation . Daftar resmi method tag tersedia di dokumentasi Cache .