Table of Contents
When building something in the Laravel framework, your application might require filtering query results dynamically based on the user’s request parameters.
This tutorial will demonstrate the process of filtering query results using pipelines while keeping your code clean and readable.
Understanding Laravel Pipeline
A pipeline is a design pattern in OOP specifically designed for handling complex mutation of an object where the object is passed through each task (such as passing a pipe) and returns the final transformed object after executing all tasks.
In other words, the Laravel pipeline breaks down the huge complex processes of manipulating objects into smaller individual pieces that are responsible for processing and passing data to the next step. As a result, the code becomes easier to maintain and reusable.
Let’s imagine a situation where we are not aware of pipelines, we would normally consider setting up a controller and filter query using a conventional if statement.
class PostController
{
public function index(Request $request)
{
$query = Post::query();
if ($request->has('status')) {
$query->where('status', $request->status);
}
if ($request->has('orderBy')) {
$query->orderBy('created_at, $request->orderBy);
}
// And probably all other filters
$posts = $query->get();
return view('post.index', compact('posts'));
}
}
Although this approach might do the job, this will get messy pretty quickly if you have longer multiple conditions.
Filtering Query using Pipelines
This implementation has a major advantage over the approach previously stated above, its ability to handle an ever-growing number of conditions without compromising the code maintainability.
So, let’s try to refactor the if statement approach mentioned above using pipelines.
First, for pipelines to work, we need to create a filter class. So, let’s create a custom filter class inside app/QueryFilters
directory.
Next, Our filter class should have a handle
method that contains the logic to filter the query.
# app/QueryFilters/Status.php
has('status')) {
$query->where('status', request('status'));
}
$next($builder);
}
}
This handle
method will receive two parameters, the first object is what we pass through our pipeline and the second is a closure function that will move forward to our next filter.
Pipeline ships with Laravel so you don’t need to install any package. Therefore, we simply import Illuminate\Pipeline\Pipeline
and instantiate using app()
.
use Illuminate\Pipeline\Pipeline;
class PostController
{
public function index(Request $request)
{
$query = Post::query();
$posts = app(Pipeline::class)
->send($query)
->through([
\App\QueryFilters\Status::class,
\App\QueryFilters\OrderBy::class,
])
->thenReturn()
->get();
return view('post.index', compact('posts'));
}
}
- The
send()
method receives the actual object to send through the pipelines.
- The
through()
method receives an array of filters (known as pipes).
- The
thenReturn()
method runs the pipeline and returns the result (which will return the initial query object).
Laravel pipeline gives you a very flexible approach to filtering queries. Whether deciding to use it or not depends upon the complexity of your project, but this short tutorial will give you an idea to make good use of pipelines.
I hope this has been informative and gives you an understanding of how to use Laravel pipelines. You can check out more about pipelines and how they work at Laravel official docs.
How about using $query->when()?
Thanks for the comment. That is also one way to do it but I think it’ll soon get convoluted after it reaches multiple numbers of checks. Hence, Pipeline to the rescue. 🙂
Hi, when the handle method of the filter is displayed, $builder variable refers to $query, which can be typed as Illuminate\Database\Eloquent\Builder just like $next can be typed as callable, and handle method returns type as Builder.
Important: You need to return $next($query); to get the builder when use ->thenReturn()
nice post btw!