Writing clean, composable Eloquent Filters
Composing Query Builders with Functions 😎
If you’ve been following my medium publications on Laravel, you’d know my journey started about 6 months ago. In this time, I have learned loads about PHP, Laravel, Eloquent, Query Builders, and much more.
In this post, I’ll be introducing a method I use at work to make filtering and sorting collections easy. Thanks, Michalis Antoniou for showing me this.
“Why re-invent laravel filters? 😱”, you might ask.
No, I’m not trying to. A lot of filter composition happens on the projects I work on. The code needs to be able to filter by this and that, and sort by this other parameter too.
It gets complicated fast, and we’re only making sure our code remains simple no matter how complicated the filters get.
For the sake of this article, I’ll be assuming you have worked with Laravel and have used Eloquent to work with collections.
Let’s begin 💪
Imagine that this URL and query strings:
/users?name=myk&age=21&company=rick-and-morty&sort_age=desc
automatically knew to filter the DB query by showing users that have their
name containing
myk
age as
21
company name containing
rick-and-morty
and order the user records by age
in descending order.
Also, imagine that the eloquent query filter for this, looked something like:
See how each query string corresponds to a method that handles it? 😍
rather than …
this is hard on the eye, I must say 😩
The previous method presents a clean, composable, easy-to-understand approach to filtering collections in Laravel, and that’s what we’ll be learning to achieve. 🔥
Introducing QueryFilters 😍
You may have noticed that the UserFilters.php
class extends QueryFilters.php
, which is a base class (one we inherit from) that contains the logic that makes this type of filtering possible.
The apply(…) method holds the magic sauce 😜
The filters()
method returns an associative array contain input values in the url and body of the request. So, it’ll contain query strings and their values, as well as key-values within the request body (for POST and PUT requests).
The apply(Builder $builder)
method loops through the associative array returned by filters()
and
- if the inheriting class has a method with the name of the request key, it executes that method, passing it a parameter with the request value if exists.
So, how do we use this?
Introducing the Filterable Trait 😀
This trait is to be used in every Eloquent Model you intend to be filterable.
In our case, we’ll be using it in our User
model like:
<?php
use App\Filters\Filterable;
class User {
use Filterable;
...
}
Let’s work on our UserController 👇
Now, within our UserController.php, or any other controller in fact, we can inject UserFilters
into any method and have Laravel’s dependency injection manager fill that for us.
Here’s a simple UserController that returns a list of users via its index()
method:
Now, we celebrate 🍾🎉
If you load the endpoint that UserController’s index() method serves, you should be able to use variations of the query strings in this URL:
/users?name=myk&age=21&company=rick-and-morty&sort_age=desc
and see its effect on the response. 🔥🔥🔥
I really like this approach because it allows one build really complex filters while keeping the code clean and reusable. We already have at work, and still do.
I hope you enjoy using this.
I look forward to your feedback in the comments. 😍😍😍