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

> Leer hoe je met het cachesysteem van Laravel de prestaties van je applicatie verbetert.

## Wat is caching?

Databasequery's en aanroepen naar externe API's zijn duur qua CPU en netwerk en kunnen meerdere seconden duren.
Als je dezelfde data vaker ophaalt, kun je het resultaat in de **cache** opslaan zodat volgende requests snel verwerkt worden.

Laravel biedt een uniforme API die diverse cache-backends ondersteunt, zoals Memcached, Redis, DynamoDB en de database.

<Info>
  Standaard is de `database`-driver geconfigureerd. Door over te schakelen naar Redis of Memcached krijg je een nog snellere cache.
</Info>

## Cacheconfiguratie

### config/cache.php

De cacheconfiguratie staat gebundeld in `config/cache.php`.
Met de omgevingsvariabele `CACHE_STORE` schakel je tussen standaarddrivers.

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

### Beschikbare drivers

<AccordionGroup>
  <Accordion title="database (standaard)">
    Slaat geserialiseerde cachedata op in een databasetabel.
    Nieuwe projecten vanaf Laravel 11 bevatten de migration standaard.

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

    Als de migration ontbreekt, maak je hem aan met een Artisan-commando.

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

  <Accordion title="file">
    Slaat cachedata op in het bestandssysteem.
    Geen extra setup nodig, geschikt voor kleine applicaties.

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

  <Accordion title="redis">
    Een snelle cachedriver die in-memory werkt.
    Wordt in productieomgevingen het meest gebruikt. Vereist de PhpRedis PHP-extensie of het pakket `predis/predis`.

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

  <Accordion title="memcached">
    Vereist het Memcached PECL-pakket.
    Configureer de servers in `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">
    Gebruikt AWS DynamoDB als cache store.
    Maak vooraf een DynamoDB-tabel aan en installeer de 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">
    Gebruikt een willekeurige filesystem-disk als key/value-cachestore.
    Handig als je een bestaande S3-disk direct als cache wilt gebruiken.

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

  <Accordion title="array / null (voor testen)">
    `array` is een in-memory cache die alleen binnen de request geldig is.
    `null` negeert alle bewerkingen. Beide zijn handig bij geautomatiseerde tests.

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

### Hiërarchie van cachedrivers

Kies de driver op basis van toepassing en snelheid.

```mermaid theme={null}
flowchart LR
    A["Applicatie"] --> B["Cache-facade"]

    subgraph L1 ["L1: binnen de request (snelst)"]
        C["array<br>voor testen en ontwikkeling"]
        D["memo (wrapper)<br>minder dubbele toegang"]
    end

    subgraph L2 ["L2: in-memory (snel)"]
        E["Redis<br>aanbevolen voor productie"]
        F["Memcached<br>gedistribueerde cache"]
    end

    subgraph L3 ["L3: persistente opslag (standaard)"]
        G["database (standaard)"]
        H["file"]
        I["DynamoDB (AWS)"]
        J["storage<br>disk zoals S3"]
    end

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

## Basisbewerkingen

### De Cache-facade gebruiken

Gebruik de `Cache`-facade om met de cache te werken.

```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 [
            // ...
        ];
    }
}
```

Om meerdere cache stores te gebruiken, gebruik je de methode `store()`.

```php theme={null}
$value = Cache::store('file')->get('foo');

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

### Data ophalen: `Cache::get()`

Met de methode `get()` haal je data uit de cache.
Als de cache niet bestaat, wordt `null` teruggegeven. Je kunt ook een standaardwaarde opgeven.

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

// Standaardwaarde opgeven
$value = Cache::get('key', 'default');

// Standaardwaarde lazy ophalen met een closure
$value = Cache::get('key', function () {
    return DB::table('settings')->get();
});
```

### Data opslaan: `Cache::put()`

Met de methode `put()` sla je data op in de cache. Als derde argument geef je de geldigheidsduur (in seconden) op.

```php theme={null}
// 10 seconden bewaren
Cache::put('key', 'value', 10);

// Geldigheidsduur opgeven met een Carbon-instantie
Cache::put('key', 'value', now()->plus(minutes: 10));

// Zonder vervaltijd (permanent bewaren)
Cache::put('key', 'value');
```

Er is ook een methode `add()` die alleen opslaat als de cache nog niet bestaat.

```php theme={null}
// Alleen toevoegen als de key niet bestaat (atomische bewerking)
Cache::add('key', 'value', $seconds);
```

Om permanent op te slaan gebruik je `forever()`.

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

### Ophalen of opslaan: `Cache::remember()`

Dit is de **meest gebruikte bewerking**. Als de data in de cache staat, wordt die teruggegeven; zo niet, dan wordt de closure uitgevoerd en het resultaat opgeslagen.

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

<Tip>
  Met `Cache::remember()` schrijf je de drie stappen "cache controleren → ophalen als hij ontbreekt → opslaan in de cache" in één regel. Ideaal voor het cachen van databasequeryresultaten en responses van externe API's.
</Tip>

### De flow van remember()

```mermaid theme={null}
flowchart TD
    A["Cache::remember('key', $ttl, fn)"] --> B{"Bestaat 'key'<br>in de cache store?"}
    B -->|"Hit"| C["Data uit de cache ophalen"]
    B -->|"Miss"| D["Closure fn uitvoeren<br>bijv. DB-query / API-aanroep"]
    D --> E["Resultaat in de cache opslaan<br>geldigheidsduur = $ttl seconden"]
    E --> F["Data teruggeven"]
    C --> F
```

Er is ook een permanente variant: `rememberForever()`.

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

Wil je weten of de cache een "hit" was of dat de waarde nieuw is opgehaald via de closure, gebruik dan de methode `rememberWithWarmth()`. Deze methode geeft een array terug met de cachewaarde en een boolean die aangeeft of de waarde warm was (uit de cache kwam).

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

if ($warm) {
    // Data uit de cache
} else {
    // Data nieuw opgehaald via de closure
}
```

<Tip>
  `rememberWithWarmth()` is handig als je de effectiviteit van je cache wilt monitoren. Is \$warm vaak `false`, dan kun je dat mogelijk verbeteren door de geldigheidsduur (TTL) van de cache aan te passen.
</Tip>

#### Stale While Revalidate (flexibele cachevernieuwing)

Bij `Cache::flexible()` geef je met een array een "verse periode" en een "periode waarin verouderde data nog bruikbaar is" op.
Dit is het patroon waarbij je verouderde data aan de gebruiker teruggeeft terwijl de cache op de achtergrond wordt vernieuwd.

```php theme={null}
// [verse periode (sec), periode waarin verouderd nog bruikbaar is (sec)]
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});
```

### Bestaan controleren: `Cache::has()`

```php theme={null}
if (Cache::has('key')) {
    // Afhandeling wanneer de cache bestaat
}
```

### Waarden verhogen / verlagen

Je kunt integer-tellers bijwerken.

```php theme={null}
// Initialiseren als de waarde niet bestaat
Cache::add('key', 0, now()->addHours(4));

// Verhogen / verlagen
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
```

### Ophalen en verwijderen: `Cache::pull()`

Haalt de waarde op en verwijdert hem daarna uit de cache. Handig voor het beheren van eenmalige data.

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

### Data verwijderen: `Cache::forget()`

```php theme={null}
// Een specifieke key verwijderen
Cache::forget('key');

// De hele cache wissen
Cache::flush();
```

<Warning>
  `Cache::flush()` verwijdert alle entries, ongeacht de "prefix"-instelling van de cache. Wees voorzichtig als de cache door meerdere applicaties wordt gedeeld.
</Warning>

Met `Cache::flushLocks()` kun je alle atomische locks in de cache wissen.

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

### TTL verlengen: `Cache::touch()`

Verlengt de geldigheidsduur van een bestaand cache-item.

```php theme={null}
// In seconden opgeven
Cache::touch('key', 3600);

// Met een Carbon-instantie opgeven
Cache::touch('key', now()->addHours(2));
```

## Cache-memoization

Met de `memo`-driver worden cachetoegangen binnen dezelfde request in het geheugen gecachet.
Bij veel herhaalde toegang tot dezelfde key vermindert dit het aantal roundtrips naar de cache store.

```php theme={null}
use Illuminate\Support\Facades\Cache;

// De standaardstore memoizen
$value = Cache::memo()->get('key');

// De Redis-store memoizen
$value = Cache::memo('redis')->get('key');
```

```php theme={null}
// De eerste toegang gaat naar de cache store
$value = Cache::memo()->get('key');

// Vanaf de tweede keer komt de waarde uit het geheugen (geen toegang tot de store)
$value = Cache::memo()->get('key');
```

## Cachetags

Je kunt gerelateerde cache-items groeperen met tags en ze in één keer verwijderen.

<Warning>
  Cachetags zijn niet beschikbaar met de drivers `file`, `dynamodb`, `database` en `storage`. Je hebt de `redis`- of `memcached`-driver nodig.
</Warning>

### Structuur van getagde cache

Cache-items met meerdere tags kun je via elk van die tags gebundeld verwijderen.

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

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

    T1 -->|"flush()"| E["John en Anne verwijderen"]
    T2 -->|"flush()"| F["Alleen John verwijderen"]
    T3 -->|"flush()"| G["Alleen Anne verwijderen"]
```

### Getagde cache opslaan en ophalen

```php theme={null}
use Illuminate\Support\Facades\Cache;

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

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

### Getagde cache verwijderen

```php theme={null}
// Alle cache met de tags 'people' en 'authors' verwijderen (zowel John als Anne)
Cache::tags(['people', 'authors'])->flush();

// Alleen de tag 'authors' verwijderen (alleen Anne; John blijft)
Cache::tags('authors')->flush();
```

<Tip>
  Tags zijn handig wanneer je de cache per groep wilt invalideren, bijvoorbeeld per gebruiker of per artikel.
  Voorbeeld: met `Cache::tags(['user', "user:{$userId}"])->flush()` wis je alle cache van die gebruiker.
</Tip>

## Atomische bewerkingen (locks)

Met `Cache::lock()` kun je gedistribueerde locks implementeren en race conditions door meerdere processen of parallelle requests voorkomen.

<Info>
  Deze functionaliteit is beschikbaar met de cachedrivers `memcached`, `redis`, `dynamodb`, `database`, `file` en `array`. Alle servers moeten met dezelfde centrale cacheserver communiceren.
</Info>

### Basislocks

```php theme={null}
use Illuminate\Support\Facades\Cache;

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

if ($lock->get()) {
    // Lock succesvol verkregen (10 seconden geldig)

    // Voer een exclusieve bewerking uit

    $lock->release();
}
```

Geef je een closure door, dan wordt de lock na afloop automatisch vrijgegeven.

```php theme={null}
Cache::lock('foo', 10)->get(function () {
    // Lock wordt automatisch vrijgegeven
});
```

### Wachten op een lock

Wacht maximaal het opgegeven aantal seconden totdat de lock verkregen kan worden. Bij een time-out wordt een `LockTimeoutException` gegooid.

```php theme={null}
use Illuminate\Contracts\Cache\LockTimeoutException;

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

try {
    $lock->block(5); // Maximaal 5 seconden wachten

    // Verwerking na het verkrijgen van de lock

} catch (LockTimeoutException $e) {
    // Lock verkrijgen mislukt
} finally {
    $lock->release();
}
```

Met een closure schrijf je het eenvoudiger.

```php theme={null}
Cache::lock('foo', 10)->block(5, function () {
    // Maximaal 5 seconden wachten op de lock; na afloop automatisch vrijgeven
});
```

### Dubbele uitvoering voorkomen: `withoutOverlapping()`

Een eenvoudige manier om te voorkomen dat dezelfde taak meerdere keren tegelijk draait.

```php theme={null}
Cache::withoutOverlapping('foo', function () {
    // Er draait er maar één tegelijk
});

// Wachttijd en lockduur aanpassen
Cache::withoutOverlapping('foo', function () {
    // ...
}, lockFor: 120, waitFor: 5);
```

### Aantal gelijktijdige uitvoeringen beperken: `funnel()`

Beperkt het aantal taken dat gelijktijdig mag draaien.

```php theme={null}
Cache::funnel('foo')
    ->limit(3)          // Maximaal 3 parallelle uitvoeringen toestaan
    ->releaseAfter(60)  // Na 60 seconden automatisch vrijgeven
    ->block(10)         // Maximaal 10 seconden wachten
    ->then(function () {
        // Verwerking wanneer een plek is bemachtigd
    }, function () {
        // Verwerking wanneer geen plek beschikbaar was
    });
```

### Locks doorgeven tussen processen

Een voorbeeld waarbij je een lock verkrijgt in een webrequest en vrijgeeft in een queue-job.

```php theme={null}
// Lock verkrijgen in de request en de job dispatchen
$lock = Cache::lock('processing', 120);

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

// De lock vrijgeven binnen de queue-job
Cache::restoreLock('processing', $this->owner)->release();
```

Om een lock geforceerd vrij te geven ongeacht de huidige eigenaar, gebruik je de methode `forceRelease`.

```php theme={null}
Cache::lock('processing')->forceRelease();
```

### Een lock verversen

Om de geldigheidsduur van een lock die je momenteel vasthoudt te verlengen, gebruik je de methode `refresh`. Geef je geen aantal seconden op, dan wordt de oorspronkelijke geldigheidsduur van het verkrijgen gebruikt. Handig bij langdurige taken waarbij je een korte lock periodiek wilt verlengen, zodat je niet vanaf het begin een extreem lange geldigheidsduur hoeft in te stellen.

```php theme={null}
$lock = Cache::lock('generate-reports', 60);

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

        // De lock met nog eens 60 seconden verlengen
        $lock->refresh();
    }

    $lock->release();
}
```

## De cachehelper

Met de helperfunctie `cache()` schrijf je dezelfde bewerkingen als met de `Cache`-facade eenvoudiger.

```php theme={null}
// Waarde ophalen
$value = cache('key');

// Waarde opslaan (met geldigheidsduur)
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));

// Zonder argumenten dezelfde instantie als de facade krijgen
cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});
```

## Praktijkvoorbeelden

### Databasequeryresultaten cachen

<Steps>
  <Step title="Cache het queryresultaat in de controller">
    ```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="Verwijder de cache wanneer de data wordt bijgewerkt">
    ```php theme={null}
    public function store(Request $request): RedirectResponse
    {
        User::create($request->validated());

        // Cache verwijderen zodat bij de volgende toegang opnieuw wordt opgehaald
        Cache::forget('all-users');

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

### Eloquent-modellen cachen

```php theme={null}
use App\Models\Product;
use Illuminate\Support\Facades\Cache;

// Productlijst per categorie 1 uur cachen
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');
}
```

### API-responses cachen

```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 minuten cachen
        function () use ($city) {
            $response = Http::get('https://api.weather.example.com/current', [
                'city' => $city,
            ]);

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

### Groepsbeheer met cachetags

```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());

        // Alle cache van dit artikel wissen
        Cache::tags(["article:{$article->id}"])->flush();

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

## Samenvatting

<AccordionGroup>
  <Accordion title="Overzicht van veelgebruikte methoden">
    | Methode                            | Beschrijving                       |
    | ---------------------------------- | ---------------------------------- |
    | `Cache::get('key')`                | Cache ophalen                      |
    | `Cache::put('key', $value, $ttl)`  | Cache opslaan                      |
    | `Cache::remember('key', $ttl, fn)` | Ophalen of opslaan (belangrijkste) |
    | `Cache::forget('key')`             | Cache verwijderen                  |
    | `Cache::has('key')`                | Controleren of de cache bestaat    |
    | `Cache::flush()`                   | Alle cache wissen                  |
    | `Cache::forever('key', $value)`    | Permanent opslaan                  |
    | `Cache::pull('key')`               | Ophalen en daarna verwijderen      |
    | `Cache::increment('key')`          | Getal verhogen                     |
    | `Cache::tags([...])->flush()`      | Getagde cache wissen               |
  </Accordion>

  <Accordion title="Een driver kiezen">
    * **Ontwikkeling en kleinschalig**: `file` of `database`
    * **Productie en veel verkeer**: `redis` (in combinatie met Laravel Horizon ook eenvoudig te monitoren)
    * **AWS-omgeving**: `dynamodb` of `storage` (bij hergebruik van een S3-disk)
    * **Testen**: `array` of `null`
  </Accordion>

  <Accordion title="Best practices voor caching">
    * Geef cachekeys namen die uniek zijn binnen de hele app (bijv. `users:1:profile`)
    * Invalideer de cache met `Cache::forget()` of `Cache::tags()->flush()` wanneer de data wordt bijgewerkt
    * Stel de TTL van de cache niet te kort en niet te lang in, afgestemd op hoe vaak de data verandert
    * Met `Cache::remember()` schrijf je de afhandeling van een cache-miss beknopt
    * Gebruik in productie Redis en overweeg ook een failover-configuratie
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Pulse](/nl/pulse.md)
- [MongoDB](/nl/mongodb.md)
- [Upgradegids van Laravel 12 naar 13](/nl/blog/upgrade-12-to-13.md)
- [Laravel-updates van maart 2026](/nl/blog/changelog/202603.md)
- [Configuratie](/nl/configuration.md)
