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

> Come migliorare le prestazioni dell'applicazione usando il sistema di cache di Laravel.

## Cos'è la cache

Le interrogazioni al database e le chiamate ad API esterne hanno un alto costo in CPU e rete e possono impiegare diversi secondi.
Se recuperi ripetutamente gli stessi dati, salvare il risultato in **cache** ti permette di gestire le richieste successive molto più velocemente.

Laravel offre un'API unificata compatibile con vari backend di cache: Memcached, Redis, DynamoDB, database e altri.

<Info>
  Di default è configurato il driver `database`. Passando a Redis o Memcached ottieni una cache ancora più veloce.
</Info>

## Configurazione della cache

### config/cache.php

Le impostazioni di cache sono concentrate in `config/cache.php`.
Cambia il driver predefinito con la variabile d'ambiente `CACHE_STORE`.

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

### Driver disponibili

<AccordionGroup>
  <Accordion title="database (predefinito)">
    Salva i dati serializzati in una tabella del database.
    Dai nuovi progetti Laravel 11 in poi la migration è già inclusa.

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

    Se la migration non è inclusa, creala con Artisan.

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

  <Accordion title="file">
    Salva i dati di cache sul filesystem.
    Non richiede setup aggiuntivo, adatto per applicazioni piccole.

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

  <Accordion title="redis">
    Driver di cache veloce, in-memory.
    Il più usato in produzione. Serve l'estensione PHP PhpRedis o il pacchetto `predis/predis`.

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

  <Accordion title="memcached">
    Serve il pacchetto PECL Memcached.
    Configura i server 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">
    Usa AWS DynamoDB come cache store.
    Crea prima la tabella DynamoDB e installa 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">
    Usa un qualsiasi disco filesystem come cache store chiave/valore.
    Utile per riutilizzare un disco S3 esistente come cache.

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

  <Accordion title="array / null (per test)">
    `array` è una cache in-memory valida solo per la richiesta corrente.
    `null` ignora ogni operazione. Entrambi utili nei test automatizzati.

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

### Gerarchia dei driver

Usa i driver in base allo scopo e alla velocità.

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

    subgraph L1 ["L1: Nella richiesta (il più veloce)"]
        C["array<br>test/sviluppo"]
        D["memo (wrapper)<br>Riduce accessi ripetuti"]
    end

    subgraph L2 ["L2: In-memory (veloce)"]
        E["Redis<br>Consigliato in produzione"]
        F["Memcached<br>Cache distribuita"]
    end

    subgraph L3 ["L3: Storage persistente (standard)"]
        G["database (predefinito)"]
        H["file"]
        I["DynamoDB (AWS)"]
        J["storage<br>Disco come S3"]
    end

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

## Operazioni di base

### Ottenere la facade Cache

Usa la facade `Cache` per manipolare la 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 [
            // ...
        ];
    }
}
```

Per usare più store distinti usa `store()`.

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

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

### Lettura: `Cache::get()`

`get()` legge un dato dalla cache.
Se manca restituisce `null`. Puoi indicare un valore predefinito.

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

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

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

### Scrittura: `Cache::put()`

`put()` salva un dato. Il terzo argomento è la validità in secondi.

```php theme={null}
// Salva per 10 secondi
Cache::put('key', 'value', 10);

// Validità tramite istanza Carbon
Cache::put('key', 'value', now()->plus(minutes: 10));

// Senza scadenza (persistente)
Cache::put('key', 'value');
```

Con `add()` salvi solo se la chiave non esiste (operazione atomica).

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

Per salvare in modo permanente usa `forever()`.

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

### Leggi o salva: `Cache::remember()`

**L'operazione più usata**. Se la chiave è in cache, restituisce quel valore; altrimenti esegue la closure e salva il risultato.

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

<Tip>
  Con `Cache::remember()` scrivi i tre passaggi "controlla cache → altrimenti calcola → salva in cache" in una sola riga. Ideale per query DB e risposte API.
</Tip>

### Flusso di remember()

```mermaid theme={null}
flowchart TD
    A["Cache::remember('key', $ttl, fn)"] --> B{"'key' esiste nella cache?"}
    B -->|"Hit"| C["Prendi il valore dalla cache"]
    B -->|"Miss"| D["Esegui la closure fn<br>Es. query DB / chiamata API"]
    D --> E["Salva il risultato in cache<br>TTL = $ttl secondi"]
    E --> F["Restituisci il valore"]
    C --> F
```

Esiste anche `rememberForever()` senza scadenza.

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

Se vuoi sapere se il valore proviene dalla cache o è stato appena calcolato, usa `rememberWithWarmth()`. Restituisce un array con il valore e un booleano che indica se era "caldo" (cache hit).

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

if ($warm) {
    // Valore dalla cache
} else {
    // Appena calcolato dalla closure
}
```

<Tip>
  `rememberWithWarmth()` è utile per monitorare l'efficienza della cache. Se `$warm` è spesso `false`, valuta di aumentare il TTL.
</Tip>

#### Stale While Revalidate (aggiornamento flessibile della cache)

Con `Cache::flexible()` indichi in un array il periodo "fresco" e il periodo "utilizzabile anche se vecchio".
Restituisce i dati vecchi all'utente aggiornando la cache in background.

```php theme={null}
// [periodo fresco (s), periodo utilizzabile anche se vecchio (s)]
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});
```

### Verifica dell'esistenza: `Cache::has()`

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

### Increment / decrement

Manipola valori interi.

```php theme={null}
// Inizializza se non esiste
Cache::add('key', 0, now()->addHours(4));

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

### Leggi ed elimina: `Cache::pull()`

Recupera il valore e lo elimina. Utile per dati usa-e-getta.

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

### Eliminazione: `Cache::forget()`

```php theme={null}
// Elimina una chiave
Cache::forget('key');

// Svuota tutta la cache
Cache::flush();
```

<Warning>
  `Cache::flush()` elimina tutte le voci indipendentemente dal "prefisso" impostato. Attenzione se la cache è condivisa tra più applicazioni.
</Warning>

Con `Cache::flushLocks()` elimini tutti i lock atomici.

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

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

Estende la scadenza di una voce esistente.

```php theme={null}
// Indica i secondi
Cache::touch('key', 3600);

// Oppure un'istanza Carbon
Cache::touch('key', now()->addHours(2));
```

## Memoization della cache

Il driver `memo` mantiene in memoria l'accesso alla cache durante la stessa richiesta.
Utile quando accedi ripetutamente alla stessa chiave per ridurre i round trip verso lo store.

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

// Memo dello store predefinito
$value = Cache::memo()->get('key');

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

```php theme={null}
// Primo accesso: raggiunge lo store
$value = Cache::memo()->get('key');

// Accessi successivi: restituiti dalla memoria (nessun accesso allo store)
$value = Cache::memo()->get('key');
```

## Tag della cache

Raggruppa voci correlate con dei tag ed eliminale in blocco.

<Warning>
  I tag di cache non sono disponibili con i driver `file`, `dynamodb`, `database` e `storage`. Servono `redis` o `memcached`.
</Warning>

### Struttura della cache con tag

Le voci taggate con più tag possono essere eliminate tramite uno qualunque dei tag.

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

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

    T1 -->|"flush()"| E["Elimina John e Anne"]
    T2 -->|"flush()"| F["Elimina solo John"]
    T3 -->|"flush()"| G["Elimina solo Anne"]
```

### Salvataggio e lettura

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

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

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

### Cancellazione

```php theme={null}
// Elimina tutte le voci con tag 'people' e 'authors' (elimina sia John sia Anne)
Cache::tags(['people', 'authors'])->flush();

// Elimina solo con tag 'authors' (elimina solo Anne; John resta)
Cache::tags('authors')->flush();
```

<Tip>
  I tag sono utili per invalidare gruppi (per utente, per articolo, ecc.).
  Es.: `Cache::tags(['user', "user:{$userId}"])->flush()` per svuotare la cache di uno specifico utente.
</Tip>

## Operazioni atomiche (lock)

Con `Cache::lock()` implementi lock distribuiti per evitare race condition tra processi o richieste concorrenti.

<Info>
  Disponibile con i driver `memcached`, `redis`, `dynamodb`, `database`, `file` e `array`. Tutti i server devono comunicare con lo stesso cache server centrale.
</Info>

### Lock di base

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

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

if ($lock->get()) {
    // Lock ottenuto (valido 10 secondi)

    // Esegui l'operazione esclusiva

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

Passando una closure il lock viene rilasciato automaticamente al termine.

```php theme={null}
Cache::lock('foo', 10)->get(function () {
    // Il lock viene rilasciato automaticamente
});
```

### Attesa del lock

Attende al massimo i secondi indicati per ottenere il lock. Al timeout viene lanciata `LockTimeoutException`.

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

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

try {
    $lock->block(5); // Attesa max 5 secondi

    // Elaborazione dopo aver ottenuto il lock

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

Con closure è più conciso.

```php theme={null}
Cache::lock('foo', 10)->block(5, function () {
    // Attende max 5s, esegue e rilascia
});
```

### Prevenzione dell'esecuzione sovrapposta: `withoutOverlapping()`

Modo semplice per evitare esecuzioni multiple della stessa logica.

```php theme={null}
Cache::withoutOverlapping('foo', function () {
    // Solo una alla volta
});

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

### Limitazione della concorrenza: `funnel()`

Limita quanti processi possono girare in parallelo.

```php theme={null}
Cache::funnel('foo')
    ->limit(3)          // Massimo 3 in parallelo
    ->releaseAfter(60)  // Rilascio automatico dopo 60s
    ->block(10)         // Attende max 10s
    ->then(function () {
        // Slot ottenuto
    }, function () {
        // Slot non ottenuto
    });
```

### Passaggio del lock tra processi

Ottieni il lock in una richiesta web e lo rilasci in un job in coda.

```php theme={null}
// Nella richiesta: ottieni il lock e dispatcha il job
$lock = Cache::lock('processing', 120);

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

// Nel job: rilascia il lock
Cache::restoreLock('processing', $this->owner)->release();
```

Per forzare il rilascio indipendentemente dall'owner corrente usa `forceRelease`.

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

### Rinnovo del lock

Per estendere la scadenza del lock in tuo possesso usa `refresh`. Senza argomenti usa la durata originale. Utile per elaborazioni lunghe con lock brevi che vuoi rinnovare periodicamente.

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

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

        // Rinnova per altri 60 secondi
        $lock->refresh();
    }

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

## Helper cache

L'helper `cache()` fa le stesse cose della facade in modo più conciso.

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

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

// Senza argomenti ottieni la stessa istanza della facade
cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});
```

## Esempi pratici

### Cache dei risultati di query

<Steps>
  <Step title="Cache dei risultati nel 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="Invalidare la cache all'aggiornamento">
    ```php theme={null}
    public function store(Request $request): RedirectResponse
    {
        User::create($request->validated());

        // Elimina la cache: verrà ricalcolata alla prossima lettura
        Cache::forget('all-users');

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

### Cache di modelli Eloquent

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

// Cache dell'elenco per categoria per 1 ora
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 di risposte 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 minuti
        function () use ($city) {
            $response = Http::get('https://api.weather.example.com/current', [
                'city' => $city,
            ]);

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

### Gestione a gruppi con i 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());

        // Svuota tutta la cache legata all'articolo
        Cache::tags(["article:{$article->id}"])->flush();

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

## Riepilogo

<AccordionGroup>
  <Accordion title="Metodi più usati">
    | Metodo                             | Descrizione                       |
    | ---------------------------------- | --------------------------------- |
    | `Cache::get('key')`                | Recupera la cache                 |
    | `Cache::put('key', $value, $ttl)`  | Salva in cache                    |
    | `Cache::remember('key', $ttl, fn)` | Leggi o salva (il più importante) |
    | `Cache::forget('key')`             | Elimina una voce                  |
    | `Cache::has('key')`                | Verifica l'esistenza              |
    | `Cache::flush()`                   | Svuota tutto                      |
    | `Cache::forever('key', $value)`    | Salva senza scadenza              |
    | `Cache::pull('key')`               | Recupera ed elimina               |
    | `Cache::increment('key')`          | Incrementa il valore              |
    | `Cache::tags([...])->flush()`      | Svuota le voci con tag            |
  </Accordion>

  <Accordion title="Come scegliere il driver">
    * **Sviluppo/piccole app**: `file` o `database`
    * **Produzione/alto traffico**: `redis` (con Laravel Horizon anche il monitoraggio è facile)
    * **Ambienti AWS**: `dynamodb` o `storage` (per riutilizzare S3)
    * **Test**: `array` o `null`
  </Accordion>

  <Accordion title="Best practice">
    * Naming univoco delle chiavi in tutta l'app (es. `users:1:profile`)
    * Invalida la cache con `Cache::forget()` o `Cache::tags()->flush()` quando i dati cambiano
    * Adatta il TTL alla frequenza di aggiornamento (né troppo breve né troppo lungo)
    * Con `Cache::remember()` gestisci i miss in modo conciso
    * In produzione usa Redis e valuta il failover
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Pulse](/it/pulse.md)
- [Creare un server MCP con Laravel](/it/advanced/mcp-server.md)
- [Guida all'aggiornamento da Laravel 12 a 13](/it/blog/upgrade-12-to-13.md)
- [Reset della password](/it/passwords.md)
- [Aggiornamenti Laravel di marzo 2026](/it/blog/changelog/202603.md)
