Pipeline Pattern in Laravel
PP = Pipeline Pattern
Today, we will learn the Pipeline Pattern and how to use it in Laravel. You can read in depth about PP at HERE. PP can be implemented differently in any language but we'll be seeing in PHP Laravel as Laravel already using this pattern at it's core.
What is Pipeline Pattern
Pipeline is a design pattern specifically optimized to handle stepped changes to an object. Think of an assembly line, where each step is a pipe and by the end of the line, you have your transformed object.
There are vast implementations of PP but to understand easily let's implement a filtering functionality using the pipeline pattern and Laravel.
Example
Let's say we are building filtering system in Laravel, we can filter Post by different parameters like is active or not, sort by ASC or DESC.
Before Implementing PP.
PostController.php
In the index
method, we are performing filters.
public function index(Request $request)
{
$query = Post::query();
if ($request->has('active')) {
$query->where('active', $request->input('active'));
}
if ($request->has('sort')) {
$query->orderBy('title', $request->input('sort'));
}
/* get filtered data */
$posts = $query->get();
return view('demo', compact('posts'));
}
Let's convert this mess into beautiful filter pipeline.
PostController.php
To implement Pipeline pattern, we need to make little refactoring. Index
method will looks like below
public function index(Request $request)
{
$posts = app(Pipeline::class)
->send(Post::query())
->through([
\App\Filters\Active::class,
\App\Filters\Sort::class
])
->thenReturn()
->get();
return view('demo', compact('posts'));
}
*send()
method will pass your query to handle method
*through()
method get the parameter as classes to pass through them. In simple words this is the list of filter classes
*thenReturn()
will return the final output
It's all provided by Laravel except we need to create Filter classes which being passed in through()
method.
Active class
Create Active class under app/Filters namespace.
namespace App\Filters;
use Closure;
class Active
{
public function handle($request, Closure $next)
{
if (! request()->has('active')) {
return $next($request);
}
return $next($request)->where('active', request()->input('active'));
}
}
Sort class
Create Active class under app/Filters namespace.
namespace App\Filters;
use Closure;
class Sort
{
public function handle($request, Closure $next)
{
if (! request()->has('sort')) {
return $next($request);
}
return $next($request)->orderBy('title', $request->input('sort'));
}
}
That's it.
Now if I want to add an other filter, I need to make new class let's say Published
and this class in through()
method and implement filter logic in class handle
method.
Conclusion
It might feel overwhelm to implement Pipelines just for two filters but it will be much clean and beneficial for large number of filter or any other complex implementation.