> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Cache

> Améliorez les performances de votre application avec le système de cache de Laravel.

## 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.

<Info>
  Le driver par défaut est `database`. Passer à Redis ou Memcached permet un cache encore plus rapide.
</Info>

## Configuration

### `config/cache.php`

La configuration se trouve dans `config/cache.php`.
`CACHE_STORE` change le driver par défaut.

```php theme={null}
// config/cache.php
'default' => env('CACHE_STORE', 'database'),
```

### Drivers disponibles

<AccordionGroup>
  <Accordion title="database (par défaut)">
    Stocke les données de cache sérialisées dans une table.
    Sous Laravel 11+, les migrations sont incluses par défaut.

    ```ini theme={null}
    CACHE_STORE=database
    ```

    Sinon, générez la table :

    ```shell theme={null}
    php artisan make:cache-table
    php artisan migrate
    ```
  </Accordion>

  <Accordion title="file">
    Stocke sur le système de fichiers.
    Aucune installation, convient aux petites applications.

    ```ini theme={null}
    CACHE_STORE=file
    ```
  </Accordion>

  <Accordion title="redis">
    Cache en mémoire très rapide, le plus utilisé en production.
    Nécessite l'extension PhpRedis ou le package `predis/predis`.

    ```ini theme={null}
    CACHE_STORE=redis
    REDIS_HOST=127.0.0.1
    REDIS_PORT=6379
    ```
  </Accordion>

  <Accordion title="memcached">
    Nécessite l'extension PECL Memcached.
    Configurez les serveurs dans `config/cache.php`.

    ```php theme={null}
    'memcached' => [
        'servers' => [
            [
                'host' => env('MEMCACHED_HOST', '127.0.0.1'),
                'port' => env('MEMCACHED_PORT', 11211),
                'weight' => 100,
            ],
        ],
    ],
    ```
  </Accordion>

  <Accordion title="dynamodb">
    Utilise AWS DynamoDB comme cache.
    Créez la table au préalable et installez l'AWS SDK.

    ```shell theme={null}
    composer require aws/aws-sdk-php
    ```

    ```ini theme={null}
    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
    ```
  </Accordion>

  <Accordion title="storage">
    Utilise un disque filesystem comme store clé/valeur.
    Utile pour réutiliser un disque S3 existant.

    ```php theme={null}
    'storage' => [
        'driver' => 'storage',
        'disk' => env('CACHE_STORAGE_DISK'),
        'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'),
    ],
    ```
  </Accordion>

  <Accordion title="array / null (tests)">
    `array` : mémoire limitée à la requête. `null` : ignore les opérations.
    Utile pour les tests automatisés.

    ```ini theme={null}
    CACHE_STORE=array
    ```
  </Accordion>
</AccordionGroup>

### Hiérarchie des drivers de cache

Utilisez chaque driver selon l'usage et la vitesse.

```mermaid theme={null}
flowchart LR
    A["Application"] --> B["Façade Cache"]

    subgraph L1 ["L1 : intra-requête (le plus rapide)"]
        C["array<br>Tests / dev"]
        D["memo (wrapper)<br>Réduit les accès répétés"]
    end

    subgraph L2 ["L2 : en mémoire (rapide)"]
        E["Redis<br>Recommandé en prod"]
        F["Memcached<br>Cache distribué"]
    end

    subgraph L3 ["L3 : stockage persistant (standard)"]
        G["database (par défaut)"]
        H["file"]
        I["DynamoDB (AWS)"]
        J["storage<br>Disque S3 etc."]
    end

    B --> C
    B --> D
    B --> E
    B --> F
    B --> G
    B --> H
    B --> I
    B --> J
```

## Opérations de base

### Façade Cache

Manipulez le cache via la façade `Cache`.

```php theme={null}
<?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.

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
// 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).

```php theme={null}
Cache::add('key', 'value', $seconds);
```

Pour un stockage permanent : `forever()`.

```php theme={null}
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.

```php theme={null}
$users = Cache::remember('users', 3600, function () {
    return DB::table('users')->get();
});
```

<Tip>
  `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.
</Tip>

### Flux de `remember()`

```mermaid theme={null}
flowchart TD
    A["Cache::remember('key', $ttl, fn)"] --> B{"Clé 'key' présente ?"}
    B -->|"Hit"| C["Récupérer depuis le cache"]
    B -->|"Miss"| D["Exécuter la closure<br>ex. requête DB / appel API"]
    D --> E["Enregistrer dans le cache<br>TTL = $ttl secondes"]
    E --> F["Retourner la valeur"]
    C --> F
```

Version permanente : `rememberForever()`.

```php theme={null}
$value = Cache::rememberForever('users', function () {
    return DB::table('users')->get();
});
```

Pour savoir si la valeur vient du cache, utilisez `rememberWithWarmth()`.

```php theme={null}
[$value, $warm] = Cache::rememberWithWarmth('users', 3600, function () {
    return DB::table('users')->get();
});

if ($warm) {
    // Depuis le cache
} else {
    // Nouvellement récupéré
}
```

<Tip>
  Utile pour surveiller l'efficacité du cache. Si `$warm` est souvent `false`, ajustez le TTL.
</Tip>

#### 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.

```php theme={null}
// [période fraîche(s), période stale(s)]
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});
```

### Existence : `Cache::has()`

```php theme={null}
if (Cache::has('key')) {
    // ...
}
```

### Incrément / Décrément

Manipule des compteurs entiers.

```php theme={null}
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.

```php theme={null}
$value = Cache::pull('key');
```

### Suppression : `Cache::forget()`

```php theme={null}
Cache::forget('key');

Cache::flush();
```

<Warning>
  `Cache::flush()` supprime toutes les entrées, en ignorant le préfixe. Attention en environnement partagé.
</Warning>

Videz tous les verrous atomiques avec `flushLocks()`.

```php theme={null}
Cache::flushLocks();
```

### Prolonger le TTL : `Cache::touch()`

```php theme={null}
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.

```php theme={null}
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');
```

```php theme={null}
// 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.

<Warning>
  Non pris en charge par `file`, `dynamodb`, `database`, `storage`. Nécessite `redis` ou `memcached`.
</Warning>

### Structure

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

```mermaid theme={null}
flowchart TD
    A["Cache::tags(['people', 'artists'])<br>.put('John', $data)"] --> B["Cache John"]
    C["Cache::tags(['people', 'authors'])<br>.put('Anne', $data)"] --> D["Cache Anne"]

    B --> T1["Tag : people"]
    B --> T2["Tag : artists"]
    D --> T1
    D --> T3["Tag : authors"]

    T1 -->|"flush()"| E["Supprime John et Anne"]
    T2 -->|"flush()"| F["Supprime John"]
    T3 -->|"flush()"| G["Supprime Anne"]
```

### Écriture / lecture

```php theme={null}
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

```php theme={null}
// Supprime John et Anne
Cache::tags(['people', 'authors'])->flush();

// Supprime seulement Anne
Cache::tags('authors')->flush();
```

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

## Opérations atomiques (verrous)

`Cache::lock()` implémente des verrous distribués et prévient les conditions de course.

<Info>
  Disponible sur `memcached`, `redis`, `dynamodb`, `database`, `file`, `array`. Tous les serveurs doivent parler au même cache central.
</Info>

### Verrou de base

```php theme={null}
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.

```php theme={null}
Cache::lock('foo', 10)->get(function () {
    // Libéré automatiquement
});
```

### Attente

Attend jusqu'à N secondes ; sinon lève `LockTimeoutException`.

```php theme={null}
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 :

```php theme={null}
Cache::lock('foo', 10)->block(5, function () {
    // Attend jusqu'à 5 s, libération automatique
});
```

### Prévenir l'exécution simultanée : `withoutOverlapping()`

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

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

### Limiter la concurrence : `funnel()`

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

### Passer un verrou entre processus

```php theme={null}
// 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 :

```php theme={null}
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.

```php theme={null}
$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`.

```php theme={null}
$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

<Steps>
  <Step title="Mise en cache dans le contrôleur">
    ```php theme={null}
    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');
    }
    ```
  </Step>

  <Step title="Invalidation lors d'un update">
    ```php theme={null}
    public function store(Request $request): RedirectResponse
    {
        User::create($request->validated());

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

        return redirect()->route('users.index');
    }
    ```
  </Step>
</Steps>

### Cache d'un modèle Eloquent

```php theme={null}
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

```php theme={null}
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

```php theme={null}
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

<AccordionGroup>
  <Accordion title="Méthodes fréquentes">
    | Méthode                            | Description                     |
    | ---------------------------------- | ------------------------------- |
    | `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                   |
  </Accordion>

  <Accordion title="Choix du driver">
    * **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`
  </Accordion>

  <Accordion title="Bonnes pratiques">
    * 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
  </Accordion>
</AccordionGroup>


## Related topics

- [Créer un serveur MCP avec Laravel](/fr/advanced/mcp-server.md)
- [Laravel Pulse](/fr/pulse.md)
- [Guide de mise à niveau de Laravel 12 vers 13](/fr/blog/upgrade-12-to-13.md)
- [Réinitialisation du mot de passe](/fr/passwords.md)
- [Accesseurs, mutateurs et casts Eloquent](/fr/eloquent-mutators.md)
