ArtisanPack UI
DocsSEOCaching

Caching

SEO v1.8.1

ArtisanPack UI SEO implements comprehensive caching to optimize performance. This guide covers cache configuration, usage, and management. The package...

ArtisanPack UI SEO implements comprehensive caching to optimize performance. This guide covers cache configuration, usage, and management.

Overview

The package caches:

  • SEO meta data for models
  • SEO analysis results
  • Redirect lookups
  • Sitemap content
  • Robots.txt content

Configuration

// In config/seo.php
'cache' => [
    'enabled' => true,
    'ttl' => 3600,          // 1 hour default
    'driver' => null,       // null = default driver
    'prefix' => 'seo',
],

Feature-Specific Caching

Each feature has its own cache settings:

'redirects' => [
    'cache_enabled' => true,
    'cache_ttl' => 86400,   // 24 hours
],

'sitemap' => [
    'cache_enabled' => true,
    'cache_ttl' => 3600,    // 1 hour
],

'analysis' => [
    'cache_enabled' => true,
    'cache_ttl' => 86400,   // 24 hours
],

Cache Service

Access

use ArtisanPackUI\Seo\Services\CacheService;

$cacheService = app('seo.cache');

Methods

// Get cached value
$value = $cacheService->get('meta', $model->id);

// Set cached value
$cacheService->set('meta', $model->id, $data, 3600);

// Check if cached
$exists = $cacheService->has('meta', $model->id);

// Remove from cache
$cacheService->forget('meta', $model->id);

// Clear all caches for a model
$cacheService->clearAllForModel($model);

// Clear all SEO caches
$cacheService->flush();

// Get cache key
$key = $cacheService->key('meta', $model->id);
// Returns: "seo_meta_123"

Cache Keys

The package uses structured cache keys:

Type Key Pattern Example
Meta seo_meta_{id} seo_meta_42
Analysis seo_analysis_{id} seo_analysis_42
Redirect seo:redirects:g{generation}:match:{md5(path)} seo:redirects:g01K…:match:5d41…
Sitemap {prefix}:sitemap:g{generation}:{variant}[:{type}][:page:{n}] seo:sitemap:g01K…:standard:page
Robots seo_robots seo_robots

Redirect and sitemap keys include a generation token: a ULID stored under seo:redirects:generation and {prefix}:sitemap:generation. Clearing either cache writes a new token in a single write, so every older key stops being read and expires through its TTL. A token is never reused, so overlapping clears or an evicted token key can't bring back stale entries. If the token key is missing, the next lookup starts a new one.

Automatic Cache Invalidation

The package automatically clears relevant caches when:

Model Events

// When model is updated
$post->update(['title' => 'New Title']);
// → Clears: seo_meta_42, seo_analysis_42

// When SEO meta is updated
$post->updateSeoMeta(['meta_title' => 'New']);
// → Clears: seo_meta_42

// When model is deleted
$post->delete();
// → Clears all related caches

Redirect Changes

RedirectService clears the redirect lookup cache whenever it creates, updates, deletes, imports, or bulk-updates redirects. This covers the admin interface, the API, and the seoCreateRedirect() / seoDeleteRedirect() helpers. Writes made directly on the Redirect model skip the service, so clear the cache yourself afterwards:

Redirect::where('status_code', 302)->update(['status_code' => 301]);

seoRedirect()->clearCache();

Sitemap Changes

Cached sitemap XML and llms.txt are cleared whenever a model tracked by HasSeo is saved, force deleted, or restored. seo:generate-sitemap --output also clears the cache before regenerating. After changes that skip model events, such as a bulk import, clear it yourself:

use ArtisanPackUI\SEO\Services\SitemapService;

app(SitemapService::class)->clearCache();

Manual Cache Clearing

Using Service

$cacheService = app('seo.cache');

// Clear specific cache type
$cacheService->forget('meta', $post->id);
$cacheService->forget('analysis', $post->id);

// Clear all caches for model
$cacheService->clearForModel($post);

// Flush all SEO caches
$cacheService->flush();

Using Artisan

# Clear all SEO caches
php artisan seo:clear-cache

# Clear specific type
php artisan seo:clear-cache --type=redirects
php artisan seo:clear-cache --type=sitemaps
php artisan seo:clear-cache --type=analysis

Cache Drivers

'cache' => [
    'driver' => 'redis',
],

Using File Cache

'cache' => [
    'driver' => 'file',
],

Using Database Cache

'cache' => [
    'driver' => 'database',
],

Using Array (Testing)

'cache' => [
    'driver' => 'array',
],

Cache Tags

If using a driver that supports tags (Redis, Memcached):

// The package uses these tags internally
'seo:meta'
'seo:analysis'

// Clear by tag
Cache::tags('seo:meta')->flush();
Cache::tags('seo:analysis')->flush();

Redirect and sitemap caches don't use tags. They use generation tokens, which work on every cache store, so clear them with their services instead (see Automatic Cache Invalidation).

Warming the Cache

The package provides seo:generate-sitemap and seo:submit-sitemap commands, but cache warming is application-specific. Create a custom command to warm your SEO caches:

Custom Warm Command

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\Post;

class WarmSeoCache extends Command
{
    protected $signature = 'seo:warm-cache';

    public function handle(): void
    {
        // Warm meta cache
        Post::chunk(100, function ($posts) {
            foreach ($posts as $post) {
                $post->getSeoData(); // Triggers caching
            }
        });

        // Warm sitemap cache
        app('seo.sitemap')->generate();

        // Warm analysis cache
        Post::chunk(100, function ($posts) {
            foreach ($posts as $post) {
                app('seo.analysis')->analyze($post);
            }
        });

        $this->info('SEO cache warmed.');
    }
}

On Deployment

After creating the custom command above, use it in deployment scripts:

// In a deployment script or command
Artisan::call('seo:warm-cache');

Disabling Caching

Globally

'cache' => [
    'enabled' => false,
],

Per Feature

'redirects' => [
    'cache_enabled' => false,
],

'sitemap' => [
    'cache_enabled' => false,
],

Temporarily

To bypass caching for a single operation, call the data method directly or temporarily disable caching:

// Option 1: Disable caching temporarily via config
config(['seo.cache.enabled' => false]);
$data = $post->getSeoData();
config(['seo.cache.enabled' => true]);

// Option 2: Clear cache before fetching fresh data
$cacheService->clearAllForModel($post);
$data = $post->getSeoData();

Cache Performance Tips

1. Use Redis in Production

'cache' => [
    'driver' => 'redis',
],

2. Set Appropriate TTLs

// Frequently changing data
'redirects' => [
    'cache_ttl' => 3600, // 1 hour
],

// Rarely changing data
'sitemap' => [
    'cache_ttl' => 86400, // 24 hours
],

3. Warm Cache After Bulk Operations

// After bulk import
Post::chunk(100, function ($posts) {
    foreach ($posts as $post) {
        $post->getSeoData();
    }
});

4. Clear Only What Changed

// Clear only the redirect cache after a bulk change that skips RedirectService
seoRedirect()->clearCache();

Monitoring Cache

Cache Hit Rate

Monitor your cache hit rate to ensure effectiveness. Use Laravel's built-in cache debugging:

// Use Laravel's Cache spy in tests
Cache::spy();

// Or add custom event listeners for cache hits/misses
Event::listen('cache.hit', fn ($key) => Log::debug("Cache hit: {$key}"));
Event::listen('cache.missed', fn ($key) => Log::debug("Cache miss: {$key}"));

Cache Size

For Redis:

redis-cli info memory

Events

use ArtisanPackUI\Seo\Events\SeoCacheCleared;

Event::listen(SeoCacheCleared::class, function ($event) {
    Log::info('SEO cache cleared', [
        'model' => $event->model,
        'keys' => $event->keys,
    ]);
});

Next Steps