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

# Caché

> Aprende a mejorar el rendimiento de tu aplicación con el sistema de caché de Laravel.

## Qué es la caché

Las consultas a la base de datos o las llamadas a APIs externas tienen un coste elevado en CPU y en red, y su procesamiento puede tardar varios segundos.
Si necesitas obtener los mismos datos una y otra vez, guardar el resultado en **caché** permite atender las peticiones posteriores mucho más rápido.

Laravel ofrece una API unificada compatible con distintos backends de caché: Memcached, Redis, DynamoDB, base de datos y otros.

<Info>
  Por defecto se utiliza el driver `database`. Puedes cambiarlo a Redis o Memcached para conseguir una caché aún más rápida.
</Info>

## Configuración de la caché

### config/cache.php

La configuración de la caché se centraliza en `config/cache.php`.
La variable de entorno `CACHE_STORE` determina el driver por defecto.

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

### Drivers disponibles

<AccordionGroup>
  <Accordion title="database (por defecto)">
    Guarda los datos de caché serializados en una tabla de la base de datos.
    Los nuevos proyectos con Laravel 11 y posteriores incluyen la migración desde el principio.

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

    Si no se incluye la migración, créala con Artisan.

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

  <Accordion title="file">
    Guarda los datos de caché en el sistema de ficheros.
    No requiere configuración adicional y es adecuado para aplicaciones pequeñas.

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

  <Accordion title="redis">
    Driver de caché en memoria muy rápido.
    Es el más habitual en producción. Requiere la extensión PhpRedis o el paquete `predis/predis`.

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

  <Accordion title="memcached">
    Requiere el paquete PECL de Memcached.
    Configura los servidores en `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">
    Utiliza AWS DynamoDB como almacén de caché.
    Debes crear la tabla en DynamoDB con antelación e instalar el SDK de AWS.

    ```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">
    Utiliza cualquier disco del sistema de ficheros como almacén clave/valor de caché.
    Es útil si quieres reutilizar un disco S3 existente como caché.

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

  <Accordion title="array / null (para tests)">
    `array` es una caché en memoria válida solo durante la petición.
    `null` ignora todas las operaciones. Ambos resultan útiles en pruebas automatizadas.

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

### Jerarquía de drivers de caché

Elige el driver según el uso y la velocidad que necesites.

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

    subgraph L1 ["L1: Dentro de la petición (más rápido)"]
        C["array<br>tests y desarrollo"]
        D["memo (wrapper)<br>reduce accesos duplicados"]
    end

    subgraph L2 ["L2: En memoria (rápido)"]
        E["Redis<br>recomendado en producción"]
        F["Memcached<br>caché distribuida"]
    end

    subgraph L3 ["L3: Almacenamiento persistente (estándar)"]
        G["database (por defecto)"]
        H["file"]
        I["DynamoDB (AWS)"]
        J["storage<br>discos como S3"]
    end

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

## Operaciones básicas

### Uso del facade Cache

Utiliza el facade `Cache` para interactuar con la caché.

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

Para alternar entre varios almacenes utiliza el método `store()`.

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

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

### Obtener datos: `Cache::get()`

El método `get()` obtiene datos de la caché.
Devuelve `null` si no existen. También puedes indicar un valor por defecto.

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

// Valor por defecto
$value = Cache::get('key', 'default');

// Valor por defecto perezoso mediante closure
$value = Cache::get('key', function () {
    return DB::table('settings')->get();
});
```

### Guardar datos: `Cache::put()`

El método `put()` guarda datos en la caché. En el tercer argumento se indica el TTL en segundos.

```php theme={null}
// Guardar durante 10 segundos
Cache::put('key', 'value', 10);

// Indicar el TTL con un objeto Carbon
Cache::put('key', 'value', now()->plus(minutes: 10));

// Sin caducidad (almacenamiento permanente)
Cache::put('key', 'value');
```

También existe `add()`, que guarda el valor solo si la clave no existe.

```php theme={null}
// Solo se añade si no existe (operación atómica)
Cache::add('key', 'value', $seconds);
```

Para guardar de forma permanente utiliza `forever()`.

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

### Obtener o guardar: `Cache::remember()`

Es **la operación más habitual**. Si la clave existe en la caché devuelve su valor; si no, ejecuta la closure y almacena el resultado.

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

<Tip>
  `Cache::remember()` reduce a una sola línea el patrón «comprobar la caché → si no está, obtener el valor → almacenarlo». Es ideal para cachear resultados de consultas a base de datos o respuestas de APIs externas.
</Tip>

### Flujo de `remember()`

```mermaid theme={null}
flowchart TD
    A["Cache::remember('key', $ttl, fn)"] --> B{"¿Existe 'key'<br>en el almacén de caché?"}
    B -->|"Acierto (Hit)"| C["Se devuelve el valor cacheado"]
    B -->|"Fallo (Miss)"| D["Se ejecuta la closure fn<br>p. ej. consulta DB / llamada API"]
    D --> E["Se guarda el resultado en la caché<br>TTL = $ttl segundos"]
    E --> F["Se devuelve el valor"]
    C --> F
```

También existe la versión permanente `rememberForever()`.

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

Si necesitas saber si la caché ha «acertado» o si el valor se acaba de generar en la closure, utiliza `rememberWithWarmth()`. Devuelve un array con el valor y un booleano que indica si es «cálido» (proveniente de la caché).

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

if ($warm) {
    // Datos de la caché
} else {
    // Datos recién obtenidos por la closure
}
```

<Tip>
  `rememberWithWarmth()` es útil para monitorizar la eficiencia de la caché. Si `$warm` es `false` la mayoría de las veces, puede que te convenga ajustar el TTL.
</Tip>

#### Stale While Revalidate (actualización flexible)

`Cache::flexible()` recibe un array con el «periodo de frescura» y el «periodo aceptable aunque caduco». Devuelve datos antiguos al usuario y refresca la caché en segundo plano.

```php theme={null}
// [tiempo fresco (s), tiempo aceptable aunque caduco (s)]
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});
```

### Comprobar existencia: `Cache::has()`

```php theme={null}
if (Cache::has('key')) {
    // Procesar si la caché existe
}
```

### Incrementar y decrementar valores

Puedes manipular contadores enteros.

```php theme={null}
// Inicializa el valor si no existe
Cache::add('key', 0, now()->addHours(4));

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

### Obtener y eliminar: `Cache::pull()`

Obtiene el valor y a continuación lo elimina de la caché. Es útil para datos de un solo uso.

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

### Eliminar datos: `Cache::forget()`

```php theme={null}
// Eliminar una clave concreta
Cache::forget('key');

// Vaciar toda la caché
Cache::flush();
```

<Warning>
  `Cache::flush()` elimina todas las entradas independientemente del «prefijo» de caché. Ten cuidado si compartes la caché entre varias aplicaciones.
</Warning>

`Cache::flushLocks()` limpia todos los locks atómicos almacenados en la caché.

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

### Ampliar el TTL: `Cache::touch()`

Amplía la caducidad de un elemento de caché existente.

```php theme={null}
// Indicándolo en segundos
Cache::touch('key', 3600);

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

## Memoización de caché

Con el driver `memo` puedes memorizar en memoria los accesos a la caché durante una misma petición.
Reduce los viajes de ida y vuelta al almacén de caché cuando se accede repetidas veces a la misma clave.

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

// Memoizar el almacén por defecto
$value = Cache::memo()->get('key');

// Memoizar el almacén Redis
$value = Cache::memo('redis')->get('key');
```

```php theme={null}
// El primer acceso consulta al almacén de caché
$value = Cache::memo()->get('key');

// Los accesos posteriores devuelven desde memoria (sin ir al almacén)
$value = Cache::memo()->get('key');
```

## Etiquetas de caché

Puedes agrupar elementos de caché relacionados mediante etiquetas y eliminarlos en bloque.

<Warning>
  Las etiquetas de caché no están soportadas por los drivers `file`, `dynamodb`, `database` ni `storage`. Necesitas `redis` o `memcached`.
</Warning>

### Estructura de la caché con etiquetas

Un elemento de caché con varias etiquetas puede eliminarse a través de cualquiera de ellas.

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

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

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

### Guardar y obtener con etiquetas

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

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

// Obtener indicando las etiquetas
$john = Cache::tags(['people', 'artists'])->get('John');
$anne = Cache::tags(['people', 'authors'])->get('Anne');
```

### Borrar caché por etiquetas

```php theme={null}
// Elimina todos los elementos con las etiquetas 'people' y 'authors' (borra John y Anne)
Cache::tags(['people', 'authors'])->flush();

// Elimina solo los de la etiqueta 'authors' (borra Anne; John se mantiene)
Cache::tags('authors')->flush();
```

<Tip>
  Las etiquetas son útiles cuando quieres invalidar caché por grupos (por usuario, por artículo, etc.).
  Ejemplo: `Cache::tags(['user', "user:{$userId}"])->flush()` borra todas las cachés de ese usuario.
</Tip>

## Operaciones atómicas (locks)

`Cache::lock()` permite implementar locks distribuidos para evitar condiciones de carrera entre procesos o peticiones paralelas.

<Info>
  Esta función está disponible con los drivers `memcached`, `redis`, `dynamodb`, `database`, `file` y `array`. Todos los servidores deben comunicarse con el mismo servidor central de caché.
</Info>

### Lock básico

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

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

if ($lock->get()) {
    // Lock obtenido (válido durante 10 segundos)

    // Realiza el procesamiento exclusivo

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

Si le pasas una closure, el lock se libera automáticamente cuando termina.

```php theme={null}
Cache::lock('foo', 10)->get(function () {
    // El lock se libera solo
});
```

### Esperar al lock

Espera hasta el número indicado de segundos a obtener el lock. Si se agota el tiempo se lanza `LockTimeoutException`.

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

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

try {
    $lock->block(5); // Espera hasta 5 segundos

    // Procesamiento tras obtener el lock

} catch (LockTimeoutException $e) {
    // No se ha podido obtener el lock
} finally {
    $lock->release();
}
```

Con una closure el código queda más limpio.

```php theme={null}
Cache::lock('foo', 10)->block(5, function () {
    // Espera hasta 5 segundos por el lock y lo libera al finalizar
});
```

### Evitar ejecuciones simultáneas: `withoutOverlapping()`

Una forma sencilla de impedir que un mismo proceso se ejecute varias veces a la vez.

```php theme={null}
Cache::withoutOverlapping('foo', function () {
    // Solo se ejecuta uno a la vez
});

// Personaliza el tiempo de espera y la duración del lock
Cache::withoutOverlapping('foo', function () {
    // ...
}, lockFor: 120, waitFor: 5);
```

### Limitar la concurrencia: `funnel()`

Limita el número de ejecuciones en paralelo.

```php theme={null}
Cache::funnel('foo')
    ->limit(3)          // Permite hasta 3 ejecuciones paralelas
    ->releaseAfter(60)  // Se libera automáticamente a los 60 s
    ->block(10)         // Espera hasta 10 s
    ->then(function () {
        // Bloque de código si se ha obtenido una ranura
    }, function () {
        // Bloque de código si no se ha obtenido ranura
    });
```

### Transferir un lock entre procesos

Caso típico: obtener el lock en una petición web y liberarlo desde un job de la cola.

```php theme={null}
// En la petición se obtiene el lock y se despacha el job
$lock = Cache::lock('processing', 120);

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

// Dentro del job se libera el lock
Cache::restoreLock('processing', $this->owner)->release();
```

Para liberar el lock de forma forzada, sin importar el propietario actual, utiliza el método `forceRelease`.

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

### Refrescar el lock

Con el método `refresh` puedes ampliar la validez del lock que ya tienes. Si no indicas los segundos se usa la duración con la que se creó. Es útil para procesos largos en los que quieres ir extendiendo un lock corto en lugar de configurar de entrada un TTL muy grande.

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

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

        // Amplía el lock otros 60 segundos
        $lock->refresh();
    }

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

## Helper de caché

La función auxiliar `cache()` permite hacer lo mismo que el facade `Cache` con una sintaxis más breve.

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

// Guardar un valor (con TTL)
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));

// Sin argumentos obtienes la misma instancia que con el facade
cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});
```

## Ejemplos prácticos

### Cachear resultados de consultas a la base de datos

<Steps>
  <Step title="Cachear el resultado de la consulta en el controlador">
    ```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="Cuando los datos cambien, invalida la caché">
    ```php theme={null}
    public function store(Request $request): RedirectResponse
    {
        User::create($request->validated());

        // Elimina la caché para que se regenere en la siguiente petición
        Cache::forget('all-users');

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

### Cachear modelos Eloquent

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

// Cachear una hora el listado de productos por categoría
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');
}
```

### Cachear respuestas de una 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 minutos de caché
        function () use ($city) {
            $response = Http::get('https://api.weather.example.com/current', [
                'city' => $city,
            ]);

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

### Gestión por grupos con etiquetas de caché

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

        // Limpia todas las cachés relacionadas con este artículo
        Cache::tags(["article:{$article->id}"])->flush();

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

## Resumen

<AccordionGroup>
  <Accordion title="Métodos más habituales">
    | Método                             | Descripción                          |
    | ---------------------------------- | ------------------------------------ |
    | `Cache::get('key')`                | Obtiene un valor de la caché         |
    | `Cache::put('key', $value, $ttl)`  | Guarda un valor en la caché          |
    | `Cache::remember('key', $ttl, fn)` | Obtiene o guarda (el más importante) |
    | `Cache::forget('key')`             | Elimina un valor                     |
    | `Cache::has('key')`                | Comprueba si existe                  |
    | `Cache::flush()`                   | Vacía toda la caché                  |
    | `Cache::forever('key', $value)`    | Guarda de forma permanente           |
    | `Cache::pull('key')`               | Obtiene y elimina                    |
    | `Cache::increment('key')`          | Incrementa un contador               |
    | `Cache::tags([...])->flush()`      | Vacía la caché por etiquetas         |
  </Accordion>

  <Accordion title="Cómo elegir el driver">
    * **Desarrollo o baja escala**: `file` o `database`.
    * **Producción o alto tráfico**: `redis` (combinado con Laravel Horizon, además, facilita el monitoreo).
    * **Entorno AWS**: `dynamodb` o `storage` (si reutilizas un disco S3).
    * **Tests**: `array` o `null`.
  </Accordion>

  <Accordion title="Buenas prácticas de caché">
    * Da a las claves un nombre único en toda la aplicación (por ejemplo, `users:1:profile`).
    * Cuando los datos cambien invalídalos con `Cache::forget()` o `Cache::tags()->flush()`.
    * El TTL no debe ser ni demasiado corto ni demasiado largo: ajústalo a la frecuencia con la que cambian los datos.
    * `Cache::remember()` te ayuda a escribir de forma limpia el flujo de miss.
    * En producción utiliza Redis y valora configurar failover.
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Pulse](/es/pulse.md)
- [Crear un servidor MCP con Laravel](/es/advanced/mcp-server.md)
- [Guía de actualización de Laravel 12 a 13](/es/blog/upgrade-12-to-13.md)
- [MongoDB](/es/mongodb.md)
- [Restablecimiento de contraseña](/es/passwords.md)
