Contenido del curso
Base de datos
Consultas a tablas
Sistema de inicio de sesión
Gestión de base de datos
Cómo reducir 61 consultas a una con Doctrine
Resumen
Cuando un proyecto Symfony lanza 61 consultas en el home sin razón aparente, el rendimiento se desploma. La solución pasa por optimizar el repositorio con QueryBuilder y joins para entregar toda la información desde el controlador y no desde la vista. Esta guía te muestra cómo reducir esas 61 consultas a una sola, ideal si trabajas con Doctrine y buscas escalar sin sorpresas.
¿Por qué se generan 61 consultas en el home?
El problema nace en el PageController, método index. Ahí se hace una consulta inicial para traer los fragmentos de código, pero la vista termina disparando consultas extra en cada iteración del for.
Por cada vuelta del bucle, la plantilla pide datos del usuario y cuenta comentarios. Resultado: una avalancha de consultas que la barra de navegación de Symfony marca con un número alarmante.
¿Qué es el problema N+1 en Doctrine? Es cuando una consulta inicial dispara N consultas adicionales al recorrer una colección, normalmente porque las relaciones se cargan de forma perezosa desde la vista en lugar de precargarse con un join.
La regla es clara: la vista no debe consultar la base de datos. Toda la información tiene que llegar lista desde el controlador.
¿Cómo crear un método personalizado en el repositorio?
Los repositorios en Symfony extienden de una clase base que ya trae métodos como find, findAll y otros. Antes de inventar nada, conviene leer esa documentación interna porque cada método tiene un propósito definido.
Para este caso vamos a crear un método propio apoyándonos en el QueryBuilder. El primer paso es declararlo como public function dentro del repositorio.
¿Qué hace el QueryBuilder y por qué usar un alias?
El QueryBuilder es la herramienta de Doctrine para construir consultas DQL de forma programática. Trabajamos con una variable $qb y le asignamos un alias a la entidad, en este caso una letra que represente al fragmento de código.
En vez de suponer letras al azar, escribe la palabra completa para que el código sea legible. El alias representa a la entidad dentro de la consulta.
¿Cómo añadir joins para comentarios y autor?
Aquí está el truco para matar el N+1. Necesitas dos joins:
- Un join con la relación de comentarios, porque un fragmento de código puede tener muchos comentarios.
- Un join con la relación de autor, que apunta a la entidad de usuarios.
Los nombres de las relaciones no son arbitrarios: deben coincidir exactamente con los que registraste al definir las entidades. Cada relación que añadas al join también debe sumarse al addSelect, así Doctrine trae los datos hidratados en una sola consulta.
El método se llama leftJoin o innerJoin según el caso, y addSelect agrega los campos relacionados al resultado. Sin el addSelect, el join filtra pero no precarga, y el N+1 vuelve.
¿Cómo ordenar y retornar el resultado final?
Después de los joins, se ordena el resultado de manera descendente. Como la entidad no tiene un campo de fecha listo para usar, ordenamos por id con orderBy en modo DESC.
Finalmente, se cierra la cadena con getQuery() y luego getResult(). El primero construye la consulta, el segundo ejecuta y devuelve el array de objetos.
¿Qué diferencia hay entre getQuery y getResult?
getQuery()devuelve el objeto Query listo para ejecutarse.getResult()ejecuta esa query y retorna los resultados hidratados como entidades. Siempre van encadenados al final del QueryBuilder.
Al actualizar el proyecto, la barra de navegación pasa de 61 consultas a una sola. Ese salto es la prueba de que optimizar desde el repositorio resuelve problemas que parecen estructurales.
¿Qué reto sigue para consolidar la práctica?
La misma lógica aplica a las vistas de detalle. Hoy, al visitar un ítem con dos comentarios, se disparan cinco consultas; uno con cinco comentarios, seis consultas. Ese número escala con el contenido y no debería.
El reto es replicar el patrón:
- Crea un método personalizado en el repositorio para traer un único ítem.
- Aplica los mismos joins con comentarios y autor.
- Usa
addSelectpara hidratar las relaciones en la misma consulta. - Reemplaza la llamada actual en el controlador por tu nuevo método.
Con esa mejora, cada vista de detalle también caerá a una sola consulta. ¿Te animas a compartir tu implementación en los comentarios?