Por defecto, Eloquent ORM obtiene todas las columnas de cada registro consultado. Una manera de optimizar tus consultas -sobretodo cuando requieres obtener una gran cantidad de datos- es seleccionar los campos específicos que necesitas mostrar en la vista o colocar en tus reportes. En esta lección veremos cómo lograr esto.
Esta lección incluye un video premium
Regístrate para ver este video y cientos de lecciones exclusivas.
Mira el código en GitHub: actual, resultado, comparación.
Seleccionar columnas específicas de una tabla con Eloquent
Seleccionar columnas específicas con el ORM Eloquent es muy sencillo, simplemente llama al método select
pasando como primer argumento un arreglo con los campos que quieres seleccionar:
<?php use App\Product; $products = Product::query() ->select(['title', 'slug']) ->with('category') ->get();
Nota además que estoy utilizando el método estático query
para comenzar a escribir la consulta. Esto es completamente opcional; sin embargo, en mi opinión luce mejor cuando necesitamos escribir una consulta con varias líneas, pero esto también es posible:
<?php $products = Product::select(['title', 'slug']) ->with('category') ->get();
El método select
no se encuentra en la clase del modelo, pero Laravel utiliza los métodos mágicos __call y __callStatic para comenzar a construir la nueva consulta:
<?php // Extracto de Illuminate\Database\Eloquent\Model abstract class Model { public function __call($method, $parameters) { //... return $this->forwardCallTo($this->newQuery(), $method, $parameters); } }
Si ahora cargamos la página veremos el siguiente error (al menos en Laravel 6.13, es posible que este comportamiento se mejore luego):
Este error que vimos en la serie Patrón Null Object en Laravel, sucede porque $product->category
devuelve null
.
Para poder obtener las categorías a las que un producto pertenece, necesitamos seleccionar el campo category_id
:
<?php Product::select(['title', 'slug', 'category_id']); // O: Product::query()->select(['title', 'slug', 'category_id']);
Seleccionar columnas específicas nos ayuda a reducir la cantidad de memoria que se requiere para traer cada registro – sobretodo en tablas con muchas columnas y donde requiramos obtener muchos registros; sin embargo, debemos tener cuidado porque si se nos olvida seleccionar una columna importante podemos obtener resultados inesperados. Por ejemplo, he olvidado seleccionar el campo image
y todas las imágenes están rotas. Corrijamos esto:
<?php Product::select(['title', 'slug', 'image', 'category_id']); // O: Product::query() ->select(['title', 'slug', 'image', 'category_id']);
Seleccionar columnas específicas de relaciones con Eloquent
También podemos especificar las columnas que se obtienen para cada registro relacionado, por ejemplo:
<?php // routes/web.php $products = Product::query() ->select(['title', 'slug', 'image', 'category_id']) ->with([ 'category' => function ($q) { $q->select(['id', 'title', 'slug']); } ]) ->get();
Alternativamente, podemos usar la siguiente sintaxis, como vimos en la lección nueva sintaxis para seleccionar columnas específicas con Eager Loading en Laravel 5.3:
<?php // routes/web.php $products = Product::query() ->select(['title', 'slug', 'image', 'category_id']) ->with(['category:id,title,slug']) ->get();
Presta atención al hecho de que he seleccionado el id
de la categoría. Laravel 6.13 no está haciendo esto automáticamente y es importante obtener este campo para que Eloquent cree la relación con el modelo de productos (products.category_id = product_categories.id
).
Luego de esto podemos ver las consultas SQL resultantes desde Telescope:
select `title`, `slug`, `image`, `category_id` from `products`
select `id`, `title`, `slug` from `product_categories` where `product_categories`.`id` in ( 1, 2, 3 )
Con esta optimización el tiempo de ejecución de la consulta se reduce de 6.5ms a 2.9ms aproximadamente. Aunque estos tiempos pueden variar la mejora parece ser significativa.
Sin embargo, este cambio nos trae dos posibles puntos negativos:
1. Podemos olvidar seleccionar una columna importante, puede ser difícil detectar esto si la columna olvidada puede contener valores null
.
El problema con `null` es que muchas veces puede significar más de una sola cosa.
¿`purchased_at` retorna `null` porque el ticket no ha sido comprado o porque olvidamos seleccionar la columna? pic.twitter.com/QpFl3vp1ky — Duilio Palacios (@Sileence) February 6, 2020
2. Si en el listado de productos ahora queremos mostrar la descripción -u otro campo no seleccionado- tendremos que regresar a la ruta o al controlador de productos para agregar el campo adicional.
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.
Lección anterior Tip de optimización con Eloquent #2: Evita el problema de N+1 Lección siguiente Tip de optimización con Eloquent #4: Utiliza paginación