El ORM de Laravel llamado Eloquent es una herramienta muy poderosa que, ademas de modelar los registros y sus relaciones con la base de datos, también nos brinda muchas funciones para filtrar resultados de búsquedas.
En este artículo vamos a ver un Laravel tip que nos permitirá filtrar consultas relacionadas.
Relaciones Eloquent clásicas
Comúnmente solemos relacionar nuestro modelos Eloquent de la siguiente forma:
class Author extends Model { public function books() { return $this->hasMany(Book::class); } }
Y luego solemos mostrar los resultados de la relación Eloquent de la siguiente forma:
$authors = Author::all(); foreach ($authors as $author) { foreach ($author->books as $book) { // ... } }
Más allá que esta no es la mejor forma de obtener los resultados de una relación (luego veremos la forma correcta). Pero, ¿cómo podríamos hacer si queremos obtener los libros que se escribieron en el último mes?
Una forma es agregar un filtro en la relación para obtener los libros del último mes:
public function books() { return $this->hasMany(Book::class) ->whereMonth('books.created_at', date('m')); }
Si quieres saber mas sobre filtros de fechas como whereMonth(), puedes ver el siguiente articulo, Buscar registros por fechas con Laravel Eloquent
Pero, esta no es la mejor opción para filtrar la relación ya que, siempre que obtengamos los libros serán filtrados por el mes actual.
Así que, la mejor opción es crear una nueva relación, además de la relación books()
de la siguiente forma:
public function books() { return $this->hasMany(Book::class); } public function booksThisMonth() { return $this->hasMany(Book::class) ->whereMonth('books.created_at', date('m')); }
Y luego obtener los resultados de la siguiente forma:
$authors = Author::all(); foreach ($authors as $author) { foreach ($author->booksThisMonth as $book) { // ... } }
Optimizando consulta Eloquent con relaciones
Como dije anteriormente, la forma que estamos obteniendo los autores con sus libros no es la forma de optima de hacerlo.
Eloquent nos provee el método with()
que nos permite integrar la relación de libros a cada uno de los autores, todo en consultas SQL optimizadas.
$authors = Author::with('booksThisMonth')->get();
Esta es la mejor forma de aplicar filtros a relaciones Eloquent, porque con código limpio y optimizado, estamos obteniendo los autores que tienen libros escritos en el mes actual.
Filtro dinámico en relaciones Eloquent
Por otro lado, si queremos agregar una mayor flexibilidad a nuestro filtro, podemos utilizar una variable que contenga un valor (en mi ejemplo, el numero del mes) para parametrizar el filtro. Pero, no utilizaremos nuestra relación booksThisMonth()
, si no que vamos utilizar nuestra relación books()
de la siguiente forma:
$month = 12; $authors = Author::with(['books' => function($query) use ($month) { $query->whereMonth('created_at', $month); }])->get();
No olviden utilizar use
para poder pasar la variable externa al método y así poder ser utilizarla.
Conclusión
En este Laravel tip vimos una excelente forma de aplicar filtros a nuestras relaciones Eloquent, optimizando las consultas SQL y haciendo mas limpio nuestro código.
Si te gusto, compártelo en tus redes sociales. Nos vemos en la próxima. 😉🤙
¡Esta genial! Pero no entendí mucho la parte de la ultima consulta, se cual es el resultado pero no se como funciona
La función with() podes recibir como parametro un array donde la key es le nombre de la relación y el segundo parametro es una función anónima (closure) donde podes aplicarle los filtro a la query antes que se ejecute la consulta de la relación. Por lo tanto, en el ejemplo le aplico el filtro whereMonth() con la variable que envió con use.
Espero que se haya entendido, cualquier duda, preguntame. Saludos.
hola tengo esta consulta $branchOffice = BranchOffice::with([‘city’ => function ($query) use ($request) {
$query->where(‘city’, ‘LIKE’, ‘%’ . $request->filter . ‘%’);
}])
->orwhere(‘name’, ‘ILIKE’, ‘%’ . $request->filter . ‘%’)
->orWhere(‘address’, ‘ILIKE’, ‘%’ . $request->filter . ‘%’)
->orderBy($request->sortBy, $request->sort)
->paginate($request->perPage);
pero no me filtra por la city al enviar el parametro, que tengo mal :'( ayuda por favor
estoy usando póstgres
Hola Vanessa. Raro, debería aplicar el filtro. Por las dudas, prueba pasar la variable filter, en vez de la variable $request.
Para estar segura de la query que se esta ejecutando, te recomiendo instalar el package debugbar, te va a servir mucho. Te dejo el link: https://github.com/barryvdh/laravel-debugbar
A mi me ha funcionado así:
$operators = User::whereHas(‘specializations’, function($query) use ($specializations){
$query->whereIn(‘specialization_id’, $specializations);
})->get();
Con una relación belongsToMany entre usuarios y especializaciones, usando la tabla pivote «specialization_user»
Me fue muy útil
Muchas gracias justo lo que estaba buscando