Introducción a la Caché en Laravel

- Andrés Cruz

In english
Introducción a la Caché en Laravel

Este material forma parte de mi curso y libro completo; puedes adquirirlos desde el apartado de libros y/o cursos Curso y libro Laravel 11 con Tailwind Vue 3, introducción a Jetstream Livewire e Inerta desde cero - 2024.

En cualquier sistema, es fundamental manejar la gestión de los recursos para mejorar el rendimiento en general del sistema; uno de los puntos más importantes en un sistema es sobre los tipos de peticiones que se repiten consecutivamente; un buen ejemplo de esto es un recurso rest de una rest api; por ejemplo un recurso que siempre devuelve un mismo tipo de listado, es un buen candidato para poder mejorar su desempeño; para analizar este punto suponte que tenemos un recurso que devuelve unos 10000 registros o más:

app\Http\Controllers\Api\PostController.php
class PostController extends Controller
{
    public function all(): JsonResponse
    {
        return  response()->json(Post::get());
    }
}

Podemos mejorar el desempeño del mismo, guardando estos datos en caché; en  Laravel, el manejo de la caché es una funcionalidad crucial para mejorar el rendimiento de nuestras aplicaciones web. Laravel ofrece soporte para múltiples sistemas de caché, incluyendo el almacenamiento en memoria, en disco, en base de datos e inclusive en sistemas como Redis. Esto nos permite almacenar de manera temporal datos que se consumen con frecuencia para acelerar el tiempo de respuesta de nuestras aplicaciones y con esto, consumir menos recursos.

En resumen, almacenar datos en caché reduce la cantidad de veces que se necesita procesar una solicitud repetida y optimiza el tiempo de respuesta de la aplicación, ya que los datos almacenados en caché se pueden recuperar rápidamente sin tener que ejecutar el proceso que se encarga de generar dichos datos; tradicionalmente, sería el acceso a la base de datos, pero, puede ser cualquier otro como un HTML.

Uso básico de la caché

Con la cache en Laravel, tenemos distintos métodos y opciones que podemos utilizar para adaptarlo a distintos esquemas; veamos los principales:

  • get()
  • has()
  • put()
  • putMany()
  • add()
  • pull()
  • many()
  • remember()
  • rememberForever()
  • increment()
  • decrement()
  • forever()
  • forget()
  • flush()

Todos estos métodos, están disponibles mediante el Facade de la caché; todos estos métodos en caché tienen en común que reciben una key (a excepción del método putMany()), para poder acceder a la cache; por ejemplo, si quieres guardar el detalle de un post, esta key puede ser post o post_detail por dar un ejemplo, si quieres un listado de post, puede ser posts o posts_index; y usando este key, es que podemos realizar una referencia a la caché.

Cache::get()

El método get() se usa para recuperar elementos de la caché. Si el elemento no existe en el caché, se devolverá un valor nulo:

$value = Cache::get('key');

De manera opcional, se puede pasar un segundo argumento que especifique el valor predeterminado que será utilizado si el elemento no existe:

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

Cache::has()

El método has() se usa para determinar si existe un elemento en el caché; devuelve false si el valor no existe y true en caso contrario:

Cache::has('key');

Cache::put()

El método put() acepta tres parámetros:

  1. key/clave.
  2. Duración de la caché.
  3. Los datos que se van a almacenar en caché.

Cache::put(key, data, duration)

Por ejemplo:

Cache::put('key', 'value', $seconds = 10);
// Cache::put('key', 'value', now()->addMinutes(10));

Cache::putMany()

El método putMany() almacena un array de datos en la caché, por lo demás, tiene los mismos parámetros que la función put() a excepción de la key:

Cache::putMany(Post::all(), now()->addMinutes(10));

Cache::add()

El método add() solo agrega el elemento al caché si aún no existe en el almacén de caché. El método devolverá verdadero si el elemento se agrega realmente al caché, caso contrario, devuelve falso:

Cache::add('key', 'value', now()->addMinutes(10));

Cache::pull()

El método pull() podemos usarlo si necesita recuperar un elemento del caché y luego eliminarlo; al igual que el método get(), retornará un valor nulo si el elemento no existe en el caché:

$value = Cache::pull('key');

Cache::many()

El método many() se utiliza para recuperar múltiples datos en base a un array de keys:

const $keys = [
  'post_1',
  'post_2',
  'post_3',
   ***
];
Cache::many(keys);

 

Cache::remember()

Muchas veces es posible que desee recuperar un elemento del caché, pero también almacenar un valor predeterminado si el elemento solicitado no existe. y para esto podemos utilizar el método de remember() que acepta tres parámetros:

  1. La key.
  2. Duración de la caché
  3. Los datos para recuperar si no se encuentran los mismos.
Cache::remember('posts', now()->addMinutes(10), function(){
  return Post::all();
});

En el script anterior, la función devuelve todos los posts (Post::all()) si no existen los datos en caché.

Cache::rememberForever()

Puede usar el método rememberForever() para recuperar un elemento del caché o almacenarlo para siempre (no hay un parámetro para pasar la duración de la cache) si no existe:

$value = Cache::rememberForever('posts', function () {
    return Post::all();
});

Incremento o disminución de los valores en caché

Puedes cambiar los valores de un valor entero almacenado en la caché utilizando los métodos de incremento y decremento, respectivamente:

Cache::increment('key');  //$value = 1
Cache::increment('key', $value);
Cache::decrement('key');  //$value = 1
Cache::decrement('key', $value);

Cache::forever()

El método forever() almacena los datos en la caché para siempre sin especificar ninguna duración:

Cache::forever('key', 'value');

Cache::forget()

El método forget() elimina un elemento de la caché con un parámetro clave especificado:

Cache::forget('key');

Cache::flush()

Este método borra todos los elementos de la caché:

Cache::flush();

Tipos de controladores

En Laravel, existen diferentes tipos de controladores disponibles, entre ellos se encuentran:

Archivo

El controlador de tipo archivo guarda el caché en archivos en el sistema de archivos; este es el esquema usado por defecto en Laravel y el archivo encriptado generado se encuentra en storage/framework/; no requiere configuraciones adicionales y lo podemos usar tanto en desarrollo como en producción.

CACHE_DRIVER=file

Base de datos

El controlador de tipo base de datos guarda el caché en una tabla de la base de datos; este tipo requiere configuraciones adicionales, para generar la tabla:

$ php artisan cache:table

Para la mayoría de los casos, este esquema no es el ideal ya que, con el uso de la caché usualmente se quiere liberar recursos de la base de datos y usando el driver para la caché de la base de datos, podríamos generar un cuello de botella.

CACHE_DRIVER=database

Memcached

El controlador de tipo Memcached almacena el caché en un servidor de caché Memcached; Memcached es un almacén de datos basado en memoria de alto rendimiento; por lo tanto, para poder usarlo, requiere de instalar un paquete adicional.

CACHE_DRIVER=memcached 

Redis

El controlador de tipo Redis almacena el caché en un motor de base de datos llamado Redis; Redis es una de las configuraciones más populares en Laravel al ser muy rápido; aunque, requiere de instalación y configuración de un programa externo al proyecto en Laravel.

CACHE_DRIVER=redis

Array

El controlador de tipo Array almacena los datos en un array en PHP y no requiere de instalación de programas adicionales.

CACHE_DRIVER=array

Caso práctico

Hay mucho enfoques que podemos usar al momento de usar la cache en Laravel; uno de los clásicos es, al momento de realizar una consulta hacemos los siguientes pasos:

  1. Verificamos si ya existen almacenados en la caché.
  2. Si existen, se devuelven los datos en caché.
  3. Si no existen, se realiza la consulta a la base de datos y con la respuesta, la usamos tanto para establecer en la caché y como parte de la respuesta.

Teniendo esto en mente, podemos cambiar la función de all() vista anteriormente en nuestra Rest Api para que luzca como:

app\Http\Controllers\Api\PostController.php
class PostController extends Controller
{
    public function all(): JsonResponse
    {
        if (Cache::has('posts_index2')) {
            return response()->json(Cache::get('posts_index2'));
        } else {
            $posts = Post::get();
            Cache::put('posts_index2', $posts);
            return  response()->json($posts);
        }
    }
}

Para completar el ejercicio, la caché que estamos almacenados nunca vence, podemos eliminar la misma de manera manual cuando actualizamos el post, que es el único caso en el cual es necesario eliminar la caché:

app\Http\Controllers\Dashboard\PostController.php

public function update(PutRequest $request, Post $post)
{

    $data = $request->validated();

    // image
    if (isset($data['image'])) {
        $data['image'] = $filename = time() . '.' . $data['image']->extension();
        $request->image->move(public_path('uploads/posts'), $filename);
    }
    // image

    Cache::forget('post_show_'.$post->id);
    $post->update($data);
    return to_route('post.index')->with('status', 'Post updated');
}

Por supuesto, según el propósito de la aplicación, puedes implementar diversos esquemas de almacenamiento y eliminación de la caché.

Otro factor importante es que, con el sistema de cache que definimos para el detalle de los posts, no estamos evitando la conexión al a base de datos la cual viene implícita en la ruta:

use Illuminate\Support\Facades\Cache;
***
class BlogController extends Controller
{
    public function show(Post $post): String // Post::find(<PK>)
    ***

Para evitar esto, puedes cambiar el tipo de ruta y hacer la búsqueda de manera manual solamente cuando no exista la caché:

use Illuminate\Support\Facades\Cache;
***
class BlogController extends Controller
{
    public function show(int $id /*slug*/): String
    {

        if (Cache::has('post_show_' . $id)) {
            return Cache::get('post_show_' . $id);
        } else {
            $post = Post::find($id);
            $cacheView = view('blog.show', compact('post'))->render();
            Cache::put('post_show_' . $post->id, $cacheView);
            return $cacheView;
        }

        // return view('blog.show', compact('post'));
    }
}

Finalmente, también podemos emplear la función de ayuda cache() en vez del Facade para todos los métodos anteriores, por ejemplo:

cache()->has('post_show_'.$post->id);

Tipos de controladores

En Laravel, existen diferentes tipos de controladores disponibles, entre ellos se encuentran:

Archivo

El controlador de tipo archivo guarda el caché en archivos en el sistema de archivos; este es el esquema usado por defecto en Laravel y el archivo encriptado generado se encuentra en storage/framework/; no requiere configuraciones adicionales y lo podemos usar tanto en desarrollo como en producción.

CACHE_STORE=file

Base de datos

El controlador de tipo base de datos guarda el caché en una tabla de la base de datos; este tipo requiere configuraciones adicionales, para generar la tabla:

$ php artisan cache:table

Para la mayoría de los casos, este esquema no es el ideal ya que, con el uso de la caché usualmente se quiere liberar recursos de la base de datos y usando el driver para la caché de la base de datos, podríamos generar un cuello de botella.

CACHE_STORE=database

Memcached

El controlador de tipo Memcached almacena el caché en un servidor de caché Memcached; Memcached es un almacén de datos basado en memoria de alto rendimiento; por lo tanto, para poder usarlo, requiere de instalar un paquete adicional.

CACHE_STORE=memcached 

Redis

El controlador de tipo Redis almacena el caché en un motor de base de datos llamado Redis; Redis es una de las configuraciones más populares en Laravel al ser muy rápido; aunque, requiere de instalación y configuración de un programa externo al proyecto en Laravel.

CACHE_STORE=redis

Array

El controlador de tipo Array almacena los datos en un array en PHP y no requiere de instalación de programas adicionales.

CACHE_STORE=array

Estas configuraciones se encuentran en el archivo de:

config\cache.php

'default' => env('CACHE_STORE', 'file'),

/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "apc", "array", "database", "file",
|         "memcached", "redis", "dynamodb", "octane", "null"
|
*/
***

Y a nivel de las variables de entorno:

.env

CACHE_STORE=file

Laravel: Error Class "Redis" Not Found Error

Cuando queremos emplear Redis en una nueva aplicación Laravel puede aparecer el siguiente error:

Class "Redis" Not Found Error

¿Por qué ocurre este error?

El motivo de este error suele ser que falta un paquete de Composer o una instalación de Redis que falta. En ambos casos, aparece una vez que intentas usar Redis en tu sistema para lo siguiente:

CACHE_DRIVER=redis

En base a esto, puede ocurrir alguna de las siguientes situaciones.

Paquete de composer faltante

Esta es la razón más común para tener este error. Para solucionarlo, ejecute el siguiente comando:

composer require predis/predis

Y una vez que el paquete esté instalado, debería poder usar Redis.

Falta la instalación de Redis

Si ha instalado el paquete, pero el error sigue ahí, es posible que necesite la instalación de Redis. Para instalar Redis, siga las instrucciones en el sitio web oficial de Redis.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.