Cache drivers — file, database, Redis, Memcached, array
Concept
Cache::put, get, remember, forever, forget are the core cache operations. Understanding the remember pattern is the most important — it's the idiomatic way to use cache in Laravel.
The remember pattern:
$value = Cache::remember($key, $ttl, fn() => expensiveOperation());This is cache-aside caching: read from cache, and if missing, compute and store. The closure only executes on cache misses. This is the correct pattern for 90% of Laravel caching.
Key design: Cache keys must be unique and descriptive. Use separators for namespacing: "user:{$id}:profile", "posts:featured:page:{$page}". For cache invalidation, the key must deterministically map to the cached data.
remember vs rememberForever: rememberForever stores indefinitely — but the cache driver may still evict entries under memory pressure (Redis with maxmemory-policy). Never rely on rememberForever for data that MUST be available — it's a performance cache, not a persistent store.
Cache invalidation: The hardest problem in caching. Strategies:
- TTL-based: Let entries expire naturally. Stale data within the TTL window is acceptable.
- Manual invalidation:
Cache::forget($key)after an update. Must invalidate all related keys. - Tag-based:
Cache::tags(['posts'])->flush()invalidates all tagged entries at once.
Race conditions: Between "check cache miss" and "store result", another request may also find a miss and compute the value — both store the same value, wasting work. Use atomic locks to prevent cache stampedes.
Code Example
<?php
use Illuminate\Support\Facades\Cache;
// remember — the standard pattern
$settings = Cache::remember('app:settings', 3600, function() {
return Setting::all()->pluck('value', 'key')->toArray();
});
// Keyed by resource ID
$user = Cache::remember("user:{$userId}:profile", 1800, function() use ($userId) {
return User::with('profile', 'roles')->findOrFail($userId);
});
// Keyed by request parameters (pagination)
$key = "posts:page:{$page}:per:{$perPage}:sort:{$sort}";
$posts = Cache::remember($key, 300, fn() => Post::published()->paginate($perPage));
// Cache invalidation — always forget the correct keys
class PostController extends Controller
{
public function update(Request $request, Post $post)
{
$post->update($request->validated());
// Invalidate all related cache entries
Cache::forget("post:{$post->id}");
Cache::forget("posts:page:1:per:15:sort:latest"); // known page keys
Cache::tags(['posts'])->flush(); // or: flush all post-tagged cache
return redirect()->route('posts.show', $post);
}
}
// TTL options
Cache::put('key', $value, 3600); // 3600 seconds
Cache::put('key', $value, 60 * 60); // 1 hour
Cache::put('key', $value, now()->addHour()); // Carbon instance
Cache::put('key', $value, new \DateInterval('PT1H')); // DateInterval
// Flexible cache key generation
function cacheKey(string $prefix, array $params = []): string
{
ksort($params); // normalize key order
return $prefix . ':' . md5(serialize($params));
}
$key = cacheKey('users:query', ['role' => 'admin', 'page' => 2, 'sort' => 'name']);
$users = Cache::remember($key, 300, fn() => User::where('role', 'admin')->paginate(15));