0

Cache::put, get, remember, forever, forget

Beginner5 min read·lv-20-002

Concept

Cache tags allow grouping related cache entries under one or more labels. When you flush a tag, all entries with that tag are invalidated. This solves the manual-key-tracking problem in cache invalidation.

Supported drivers: Tags only work with Redis and Memcached. The file and database drivers do NOT support tags.

Cache::tags(['tag1', 'tag2'])->put($key, $value, $ttl): Store with tags. An entry can have multiple tags.

Cache::tags(['tag1'])->get($key): Retrieve a tagged entry. Must specify the same tags used when storing.

Cache::tags(['tag'])->flush(): Invalidate ALL entries that have this tag. Entries with other tags that don't include 'tag' are unaffected.

Tag + key namespace: The key is still required — tags are additional metadata, not a replacement for keys. Two entries with the same key but different tags are separate entries.

Implementation detail: Redis tags work by storing an index. Cache::tags(['posts'])->flush() generates new tag identifiers, effectively making all old tagged entries unfindable (they expire naturally) without actually deleting them. This is an O(1) operation — fast regardless of how many entries are tagged.

Common tag patterns: ['users'] for user-related data, ['products', 'category:5'] for data related to both products and a specific category, ['user:42'] for all cached data for user 42.

Code Example

php
<?php
use Illuminate\Support\Facades\Cache;

// Storing with tags
Cache::tags(['posts'])->put('posts:featured', $featuredPosts, 3600);
Cache::tags(['posts', 'homepage'])->put('posts:latest', $latestPosts, 3600);
Cache::tags(['posts', 'user:42'])->put('posts:by:user:42', $userPosts, 1800);
Cache::tags(['user:42'])->put('user:42:stats', $stats, 7200);

// Retrieving tagged entries
$featured = Cache::tags(['posts'])->get('posts:featured');
$userPosts = Cache::tags(['posts', 'user:42'])->get('posts:by:user:42');

// using remember with tags
$posts = Cache::tags(['posts'])->remember('posts:page:1', 300, function() {
    return Post::published()->latest()->paginate(15);
});

// Flushing by tag — powerful for invalidation
// When ANY post is updated:
Cache::tags(['posts'])->flush();
// Invalidates: posts:featured, posts:latest, posts:page:1, posts:by:user:42, etc.
// Does NOT invalidate: user:42:stats (not tagged with 'posts')

// Per-user cache invalidation
// When user 42 updates their profile:
Cache::tags(['user:42'])->flush();
// Invalidates: posts:by:user:42, user:42:stats, user:42:profile, etc.
// Doesn't touch other users' cached data

// Practical example in Observer
class PostObserver
{
    public function saved(Post $post): void
    {
        // Invalidate all post listings and the specific user's posts
        Cache::tags(['posts'])->flush();
        Cache::tags(["user:{$post->user_id}"])->flush();
    }
}

// Remember: if using 'file' or 'database' driver in testing:
// Cache::tags() will throw BadMethodCallException
// Solution: use 'redis' or 'array' driver in tests
// config/cache.php or phpunit.xml:
// CACHE_DRIVER=array