Laravel is certainly one of the most used PHP frameworks today. This framework is great for fastly build a prototype to test your ideas, and it's even better when you need a well organized and more solid code to build and maintain applications distributed on a large scale.

Althrough its documentation is, in my opinion, one of the clearest and most detailed compared to those of its "competitors", Laravel behind itself hides a very large world of features that remains obscure to many and they indeed deserve more fame.

In this series of articles I will try to explain how the "discovery" of these features has helped me in everyday work with a clean and secure code, without the need to "reinvent the wheel" every time. Thanks to the tools already included in the framework we can find solutions faster and certainly less prone to the introduction of bugs.

Laravel Pipelines

One of the features that is among the most useful is the pipeline.

Basically, with a pipeline we can pass an object through a stack of tasks and get the result via a callback.

Let's consider a situation like this: suppose you need to get a list of products from a database, based on some query filters a user is applying via api or form ui.

Being unaware of the pipelines, we would usually set up a controller, get an instance of QueryBuilder of our Product model, and apply some where conditions based on the presence of a given parameter in the query string.

public function index (Request $request) { $builder = Product::query(); if ($request->has( 'foo' )) { $builder->where( 'foo' , $request->foo); } if ($request->has( 'bar' )) { $builder->where( 'bar' , '!=' , $request->bar); } if ($request->has( 'baz' )) { $builder->whereNotIn( 'baz' , $request->bar); } return $builder->get(); }

Some might say that the following solution could do the job, yet its readability and maintenability lacks of separation and clean code.

Pipelines to the rescue!

The primary drawback of this implementation, it's obviously that with the growing of filters conditions, it will grow the chain of if statements as well, compromising the maintenability of the code. How to fix this?

First, we should separate the conditions in different classes. Let's list them in an array, that we will use to collect them. Watch out, the order is important!

$pipes = [ FilterFoo::class, FilterBar::class, FilterBaz::class, ];

Then, we will pass our object (the instance of the Query Builder in this case) through a pipeline and finally get the result with a callback function:

public function index () { $pipes = [ FilterFoo::class, FilterBar::class, FilterBaz::class, ]; $builder = Product::query(); $results = app(Pipeline::class) ->send($builder) ->through($pipes) ->then( fn ($builder) => $builder->get() ); }

By design, Laravel wants that every element in our pipeline should have a method called handle , so it's good practice that these elements implement an interface.

namespace App ; use Closure ; interface Pipe { public function handle ($content, Closure $next) ; }

So this will be the final look of one of our classes:

namespace App ; use Closure ; class FilterFoo implements Pipe { public function handle ($builder, Closure $next) { if (request()->has( 'foo' )) { $builder->where( 'foo' , request()->foo); } return $next($builder); } }

The method handle used to execute the task receives two parameters, the first is the object that we passed to our pipeline, the second is a closure, where the object will be sent to run the next task.

We can even chose to use an alternative name, instead of the boring and undescriptive generic handle . To do that, we have to specify the custom name of the method when setting up our pipeline:

public function index () { $pipes = [ FilterFoo::class, FilterBar::class, FilterBaz::class, ]; $builder = Product::query(); $results = app(Pipeline::class) ->send($builder) ->through($pipes) ->via(‘customMethodName’) ->then( fn ($builder) => $builder->get() ); }

The classes FilterFoo , FilterBar and FilterBaz will handle the query filters based on some custom conditions, while leaving the controller index method clean.

Final words

As you can imagine, pipelines are very useful to keep your code clean. There are tons of ways you can exploit this feature to execute a list of operations in line.

Maybe a set of normalizations to be applied on a string. And you? Did you know about pipelines yet? How would you use them?

Stay tuned for more Laravel hidden features!