DEV Community

Cover image for Pipeline Pattern in Laravel
Abrar Ahmad
Abrar Ahmad

Posted on

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 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 PDP.

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'));
    }
Enter fullscreen mode Exit fullscreen mode

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'));
    }
Enter fullscreen mode Exit fullscreen mode

*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'));
    }
}
Enter fullscreen mode Exit fullscreen mode

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'));
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Cleaning Filter Classes

PS: We can clean filter classes by extracting common code in base class. Comment below if you want refactoring of Filter classes in next article.
Until next,
Ahmad

Discussion (11)

Collapse
rabeeaali profile image
Rabeea Ali • Edited

Another approach for this example is:

in PostController

public function index(Request $request)
{
     $posts = Post::query()
            ->postFilter()
            ->get();

     return view('demo', compact('posts'));
}
Enter fullscreen mode Exit fullscreen mode

in Post model

public function scopePostFilter($query)
{
       $query->when(
            request('active'),
            fn ($query) => $query->where('active', request('active'))
        );

        $query->when(
            request('sort'),
            fn ($query) => $query->orderBy('title', request('sort'));
        );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
abrardev99 profile image
Abrar Ahmad Author

Nice abstractions.

Collapse
rmoskal profile image
Robert Moskal

Certainly pipelines are nice. But in this example they are ruined by having to create two separate modules to implement the filters, rather then to just do it inline.

Collapse
abrardev99 profile image
Abrar Ahmad Author

I think you missed the post conclusion. Any here it is.

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.

Collapse
abrardev99 profile image
Abrar Ahmad Author

Example here is just for basic understanding of Pipeline. I won't be implement Pipeline for just two filters.

Collapse
sheikhusman545 profile image
sheikhusman545

thankyou for teaching me with the most easiest and simple example

Collapse
abrardev99 profile image
Abrar Ahmad Author

Glad you learn.

Collapse
henryonsoftware profile image
Henry Bui

I usually use Traits for some filters, but your Pipelines pattern is more clear and that is good approach if we have many filters. Big thanks

Collapse
abrardev99 profile image
Abrar Ahmad Author

Enjoy

Collapse
asantana66 profile image
asantana66

I think this is a good approach to code reuse for those about filtering by a common field in tables.
Thanks for the input!!

Collapse
aymenmohanmed profile image
aymen mohammed

I love it :)