Passer au contenu principal

Qu’est-ce que le cache

Une requête à la base ou un appel API externe est coûteux en CPU et en réseau, et peut prendre plusieurs secondes. Si les mêmes données sont récupérées à plusieurs reprises, mettre le résultat en cache accélère toutes les requêtes suivantes. Laravel propose une API unifiée pour Memcached, Redis, DynamoDB, base de données et bien d’autres backends.
Le driver par défaut est database. Passer à Redis ou Memcached permet un cache encore plus rapide.

Configuration

config/cache.php

La configuration se trouve dans config/cache.php. CACHE_STORE change le driver par défaut.
// config/cache.php
'default' => env('CACHE_STORE', 'database'),

Drivers disponibles

Stocke les données de cache sérialisées dans une table. Sous Laravel 11+, les migrations sont incluses par défaut.
CACHE_STORE=database
Sinon, générez la table :
php artisan make:cache-table
php artisan migrate
Stocke sur le système de fichiers. Aucune installation, convient aux petites applications.
CACHE_STORE=file
Cache en mémoire très rapide, le plus utilisé en production. Nécessite l’extension PhpRedis ou le package predis/predis.
CACHE_STORE=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
Nécessite l’extension PECL Memcached. Configurez les serveurs dans config/cache.php.
'memcached' => [
    'servers' => [
        [
            'host' => env('MEMCACHED_HOST', '127.0.0.1'),
            'port' => env('MEMCACHED_PORT', 11211),
            'weight' => 100,
        ],
    ],
],
Utilise AWS DynamoDB comme cache. Créez la table au préalable et installez l’AWS SDK.
composer require aws/aws-sdk-php
CACHE_STORE=dynamodb
DYNAMODB_CACHE_TABLE=cache
AWS_DEFAULT_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-key-id
AWS_SECRET_ACCESS_KEY=your-secret-key
Utilise un disque filesystem comme store clé/valeur. Utile pour réutiliser un disque S3 existant.
'storage' => [
    'driver' => 'storage',
    'disk' => env('CACHE_STORAGE_DISK'),
    'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'),
],
array : mémoire limitée à la requête. null : ignore les opérations. Utile pour les tests automatisés.
CACHE_STORE=array

Hiérarchie des drivers de cache

Utilisez chaque driver selon l’usage et la vitesse.

Opérations de base

Façade Cache

Manipulez le cache via la façade Cache.
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    public function index(): array
    {
        $value = Cache::get('key');

        return [
            // ...
        ];
    }
}
Utilisez store() pour cibler un store spécifique.
$value = Cache::store('file')->get('foo');

Cache::store('redis')->put('bar', 'baz', 600); // 10 min

Récupération : Cache::get()

get() retourne la valeur, ou null par défaut.
$value = Cache::get('key');

// Valeur par défaut
$value = Cache::get('key', 'default');

// Valeur par défaut via closure (évaluation paresseuse)
$value = Cache::get('key', function () {
    return DB::table('settings')->get();
});

Écriture : Cache::put()

put() stocke une valeur ; TTL en secondes en 3ᵉ argument.
// 10 secondes
Cache::put('key', 'value', 10);

// Carbon pour la date d'expiration
Cache::put('key', 'value', now()->plus(minutes: 10));

// Sans expiration
Cache::put('key', 'value');
add() n’écrit que si la clé n’existe pas encore (atomique).
Cache::add('key', 'value', $seconds);
Pour un stockage permanent : forever().
Cache::forever('key', 'value');

Get-or-set : Cache::remember()

L’opération la plus utilisée. Retourne la valeur en cache si présente, sinon exécute la closure et met en cache le résultat.
$users = Cache::remember('users', 3600, function () {
    return DB::table('users')->get();
});
Cache::remember() remplace le trio « vérifier / récupérer / mettre en cache » par une seule ligne. Idéal pour les résultats de DB ou d’API externes.

Flux de remember()

Version permanente : rememberForever().
$value = Cache::rememberForever('users', function () {
    return DB::table('users')->get();
});
Pour savoir si la valeur vient du cache, utilisez rememberWithWarmth().
[$value, $warm] = Cache::rememberWithWarmth('users', 3600, function () {
    return DB::table('users')->get();
});

if ($warm) {
    // Depuis le cache
} else {
    // Nouvellement récupéré
}
Utile pour surveiller l’efficacité du cache. Si $warm est souvent false, ajustez le TTL.

Stale While Revalidate

Cache::flexible() définit une période « frais » et une période « stale mais utilisable ». On sert du contenu obsolète tout en actualisant le cache en arrière-plan.
// [période fraîche(s), période stale(s)]
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});

Existence : Cache::has()

if (Cache::has('key')) {
    // ...
}

Incrément / Décrément

Manipule des compteurs entiers.
Cache::add('key', 0, now()->addHours(4));

Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);

Get-and-delete : Cache::pull()

Récupère et supprime en une opération.
$value = Cache::pull('key');

Suppression : Cache::forget()

Cache::forget('key');

Cache::flush();
Cache::flush() supprime toutes les entrées, en ignorant le préfixe. Attention en environnement partagé.
Videz tous les verrous atomiques avec flushLocks().
Cache::flushLocks();

Prolonger le TTL : Cache::touch()

Cache::touch('key', 3600);

Cache::touch('key', now()->addHours(2));

Mémoïsation du cache

Le driver memo met en cache en mémoire les accès pendant une même requête, réduisant les allers-retours vers le store.
use Illuminate\Support\Facades\Cache;

// Store par défaut mémoïsé
$value = Cache::memo()->get('key');

// Store Redis mémoïsé
$value = Cache::memo('redis')->get('key');
// Premier accès : va au store
$value = Cache::memo()->get('key');

// Suivants : depuis la mémoire
$value = Cache::memo()->get('key');

Tags de cache

Groupez des entrées par tag pour les supprimer en masse.
Non pris en charge par file, dynamodb, database, storage. Nécessite redis ou memcached.

Structure

Une entrée peut porter plusieurs tags ; supprimer un tag purge toutes les entrées associées.

Écriture / lecture

use Illuminate\Support\Facades\Cache;

Cache::tags(['people', 'artists'])->put('John', $john, $seconds);
Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);

$john = Cache::tags(['people', 'artists'])->get('John');
$anne = Cache::tags(['people', 'authors'])->get('Anne');

Suppression par tag

// Supprime John et Anne
Cache::tags(['people', 'authors'])->flush();

// Supprime seulement Anne
Cache::tags('authors')->flush();
Idéal pour invalider par groupe : Cache::tags(['user', "user:{$userId}"])->flush() purge tout ce qui concerne un utilisateur.

Opérations atomiques (verrous)

Cache::lock() implémente des verrous distribués et prévient les conditions de course.
Disponible sur memcached, redis, dynamodb, database, file, array. Tous les serveurs doivent parler au même cache central.

Verrou de base

use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('foo', 10);

if ($lock->get()) {
    // Verrou acquis (10 s)

    // Traitement critique

    $lock->release();
}
Avec closure, la libération est automatique.
Cache::lock('foo', 10)->get(function () {
    // Libéré automatiquement
});

Attente

Attend jusqu’à N secondes ; sinon lève LockTimeoutException.
use Illuminate\Contracts\Cache\LockTimeoutException;

$lock = Cache::lock('foo', 10);

try {
    $lock->block(5);

    // Une fois le verrou obtenu

} catch (LockTimeoutException $e) {
    // Timeout
} finally {
    $lock->release();
}
Version concise :
Cache::lock('foo', 10)->block(5, function () {
    // Attend jusqu'à 5 s, libération automatique
});

Prévenir l’exécution simultanée : withoutOverlapping()

Cache::withoutOverlapping('foo', function () {
    // Une seule exécution à la fois
});

// Personnalisation
Cache::withoutOverlapping('foo', function () {
    // ...
}, lockFor: 120, waitFor: 5);

Limiter la concurrence : funnel()

Cache::funnel('foo')
    ->limit(3)
    ->releaseAfter(60)
    ->block(10)
    ->then(function () {
        // Slot obtenu
    }, function () {
        // Slot indisponible
    });

Passer un verrou entre processus

// Requête : acquérir le verrou et dispatcher le job
$lock = Cache::lock('processing', 120);

if ($lock->get()) {
    ProcessPodcast::dispatch($podcast, $lock->owner());
}

// Job : libérer
Cache::restoreLock('processing', $this->owner)->release();
Libération forcée quel que soit le propriétaire :
Cache::lock('processing')->forceRelease();

Renouvellement du verrou

Prolongez un verrou en cours avec refresh (sans argument, réutilise le TTL initial). Idéal pour un traitement long sans avoir à fixer un TTL énorme au départ.
$lock = Cache::lock('generate-reports', 60);

if ($lock->get()) {
    foreach ($reports as $report) {
        $report->generate();

        // Prolonge de 60 s
        $lock->refresh();
    }

    $lock->release();
}

Helper cache()

Le helper cache() équivaut à la façade Cache.
$value = cache('key');

cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));

cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});

Exemples pratiques

Cache d’une requête DB

1

Mise en cache dans le contrôleur

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

public function index(): array
{
    $users = Cache::remember('all-users', 3600, function () {
        return DB::table('users')->orderBy('name')->get();
    });

    return compact('users');
}
2

Invalidation lors d'un update

public function store(Request $request): RedirectResponse
{
    User::create($request->validated());

    Cache::forget('all-users');

    return redirect()->route('users.index');
}

Cache d’un modèle Eloquent

use App\Models\Product;
use Illuminate\Support\Facades\Cache;

public function byCategory(int $categoryId): array
{
    $products = Cache::remember(
        "products:category:{$categoryId}",
        3600,
        fn () => Product::where('category_id', $categoryId)
            ->where('is_active', true)
            ->orderBy('name')
            ->get()
    );

    return compact('products');
}

Cache d’une réponse d’API

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

public function getWeather(string $city): array
{
    return Cache::remember(
        "weather:{$city}",
        1800, // 30 min
        function () use ($city) {
            $response = Http::get('https://api.weather.example.com/current', [
                'city' => $city,
            ]);

            return $response->json();
        }
    );
}

Gestion par tag

use Illuminate\Support\Facades\Cache;

class ArticleController extends Controller
{
    public function show(Article $article): array
    {
        $data = Cache::tags(['articles', "article:{$article->id}"])
            ->remember("article:{$article->id}:detail", 3600, function () use ($article) {
                return $article->load(['author', 'tags', 'comments']);
            });

        return compact('data');
    }

    public function update(Request $request, Article $article): RedirectResponse
    {
        $article->update($request->validated());

        // Purge tout le cache lié à cet article
        Cache::tags(["article:{$article->id}"])->flush();

        return redirect()->route('articles.show', $article);
    }
}

Récapitulatif

MéthodeDescription
Cache::get('key')Lecture
Cache::put('key', $value, $ttl)Écriture
Cache::remember('key', $ttl, fn)Get-or-set (la plus importante)
Cache::forget('key')Suppression
Cache::has('key')Existence
Cache::flush()Vider
Cache::forever('key', $value)Permanent
Cache::pull('key')Get + delete
Cache::increment('key')Incrémenter
Cache::tags([...])->flush()Vider par tag
  • Dev / petit projet : file ou database
  • Prod à fort trafic : redis (avec Laravel Horizon pour la supervision)
  • AWS : dynamodb ou storage (via S3)
  • Tests : array ou null
  • Clés uniques dans toute l’app (ex : users:1:profile)
  • Invalidez avec Cache::forget() ou Cache::tags()->flush() après mise à jour
  • Ni trop court ni trop long : TTL adapté à la fréquence de mise à jour
  • Cache::remember() simplifie le pattern get-or-set
  • En production, préférez Redis, avec un failover réfléchi
Dernière modification le 13 juillet 2026