Contenido del curso

Cómo eliminar el problema N+1 en Laravel

Resumen

Cuando tu aplicación Laravel hace consultas innecesarias a la base de datos, el rendimiento se degrada rápido. Aquí descubres cómo detectar el problema N+1 con Laravel Debugbar y cómo resolverlo con eager loading y verificación de relaciones cargadas en traits.

Esta guía es para desarrolladores Laravel que ya manejan controladores, modelos y relaciones Eloquent, y quieren llevar su sistema a un nivel de rendimiento estable sin importar cuántos datos crezcan.

¿Cómo detectar consultas innecesarias en Laravel?

El primer paso es medir. Sin medición no hay optimización posible, y para eso instalas un componente que te muestre exactamente qué pasa en cada request.

¿Qué es Laravel Debugbar y cómo se instala?

Laravel Debugbar es un paquete que habilita una barra inferior en el navegador para analizar consultas, rutas, vistas y más. Lo encuentras buscando debug en el repositorio de componentes de PHP [0:15].

Para instalarlo copias el comando de instalación, lo pegas en tu terminal y presionas enter. Una vez termina la descarga, se activa automáticamente y empieza a reportar el número de consultas por vista [0:55].

¿Qué es el problema N+1 en Laravel? Es cuando tu sistema ejecuta una consulta inicial y luego una consulta extra por cada registro relacionado. En lugar de 2 consultas terminas con 26 o más, y el número crece con los datos.

¿Por qué aparecen consultas en el home aunque no las hayas escrito?

Laravel tiene un sistema de sesiones propio que consulta la tabla sessions. Por eso ves consultas relacionadas con visitantes no logueados, IP y navegador, aunque tú no las hayas programado [1:30].

En el ejemplo, el home muestra 4 consultas y eso está bien. El problema aparece al visitar una pregunta: el sistema dispara 26 consultas, sube a 28 al actualizar y baja a 22 según los datos. Esa variación es la señal clara de que hay un N+1 [2:00].

¿Cómo aplicar eager loading en el controlador?

La regla de oro es que todas las consultas deben originarse en el controlador, no en traits, vistas ni comentarios. Antes de modificar nada, abre el reporte de Debugbar y revisa dónde se está consultando [2:45].

En el controlador de preguntas trabajas con el método with() para precargar relaciones. Como vas a consultar corazones del usuario logueado, defines una variable $userId (en este caso el usuario 20, porque el sistema de login se integra después).

Desde la pregunta cargas su usuario y su categoria. Esos nombres salen directamente del modelo Question, donde están definidas las relaciones hacia usuario, comentarios, categoría y respuestas [3:30].

¿Cómo cargar relaciones anidadas con closures?

Para las respuestas usas una arrow function y dentro aplicas otro with(). Le dices a la respuesta que se traiga su usuario y su sistema de corazones, pero filtrando solo los corazones del usuario logueado.

La misma lógica aplica a los comentarios de cada respuesta:

  • Cargar el usuario que creó el comentario.
  • Cargar los corazones filtrados por el usuario actual.
  • Repetir el patrón para los comentarios directos de la pregunta.
  • Cerrar pidiendo los corazones de la pregunta misma.

Después de aplicar esto, las consultas principales ya viven en QuestionController, pero todavía aparecen consultas extra disparadas desde el trait. Si agregas comentarios, el contador sigue cambiando: 23, 22, 24. Aún no es óptimo [5:30].

¿Cómo evitar consultas duplicadas dentro de un trait?

El trait de corazones se ejecuta varias veces: corazones de comentarios, corazones de comentarios de respuestas, corazones de respuestas y corazones de preguntas. Cada llamada lanza un nuevo where a la base de datos.

¿Qué hace el método relationLoaded en Laravel?

relationLoaded() es un método propio de Eloquent que verifica si una relación ya fue cargada en el modelo. Lo usas dentro del trait con un if antes de hacer la consulta [6:15].

La lógica queda así: si la relación de corazones ya está cargada y no está vacía, devuelve esa colección. Si no, entonces sí ejecuta la consulta a base de datos.

¿Cuándo sabes que optimizaste bien las consultas? Cuando el número total se mantiene constante sin importar cuántos comentarios, respuestas o corazones agregues. Si agregas datos y el contador no cambia, lograste el objetivo.

Con esa verificación, el contador baja a 14 consultas estables [7:00]. Agregas un comentario nuevo, das corazón a varios elementos, actualizas la página y se mantiene en 14.

¿Por qué quedan exactamente 14 consultas y no menos?

Dentro de esas 14 hay una consulta implícita que Laravel ejecuta al resolver el modelo desde la ruta, las consultas explícitas con sus relaciones precargadas, y la consulta clásica del sistema de sesiones. Es el piso técnico realista para esta vista.

Este patrón marca la diferencia cuando el sistema crece. Una página con 14 consultas fijas escala; una con 26 que suben a 50 colapsa.

El reto: instala Laravel Debugbar en tu propio proyecto y aplica esta misma optimización en el foro y en el blog. ¿En cuántas consultas estás ahora y a cuántas logras bajar? Cuéntalo en los comentarios.