Recently, the Laravel team added a section in the documentation about pipelines, and I think it is a good opportunity to talk about pipelines and how useful they are.
Pipelines are a design pattern that enables the creation and execution of a sequence of operations. Much like an assembly line, where each step prepares a product for the next station along the line, pipelines are a group of functions linked together, so the output of the preceding function serves as the input of the following function. Laravel employs this pattern internally, such as in middleware.
There is a Pipeline
facade which provides a couple of useful methods to pipe a given input through a series of invokable classes or closures. Here is an example:
use App\Models\User;use Illuminate\Support\Facades\Pipeline; $user = User::create([...]); $user = Pipeline::send($user) ->through([ SendWelcomeEmail::class, SubscribeToNewsletter::class, GenerateAvatar::class,]) ->then(fn (User $user) => $user);
We use send($user)
method to inject the initial data. In this case, the new created user.
The through([...])
method to provide the operations we want to execute with the input we are giving.
Finally, the then()
runs the pipeline and returns the result.
For each invokable class, two parameters are received: the input and the $next closure. Here is an example:
use Closure;use App\Models\User; class SendWelcomeEmail{ public function handle(User $user, Closure $next) { $user->notify(new InvoicePaid($invoice)); $next($user); }}
When you invoke the $next
closure, the next invokable class in the pipeline will be invoked. Additionally, there other useful methods you can use to setup the pipeline:
The pipe($pipes)
method enables you to add extra pipes into the pipeline. For example:
use App\Models\User;use Illuminate\Support\Facades\Pipeline; $user = User::create([...]); $pipeline = Pipeline::send($user) ->through([ GenerateAvatar::class, ]); if (! $user->isAdmin) { $pipeline->pipe([ SendWelcomeEmail::class, SubscribeToNewsletter::class, ]);} $user = $pipeline->then(fn (User $user) => $user);
The via($method)
method allows you to set the method to call on the pipes. By default, it is set to handle.
The thenReturn()
method runs the pipeline and automatically returns the result:
// Instead of using then()$user = Pipeline::send($user) ->through([...]) ->then(fn (User $user) => $user); // You can use thenReturn()$user = Pipeline::send($user) ->through([...]) ->thenReturn();
The setContainer()
method enables you to set the container instance, this can be useful if you want to manually instantiate the Pipeline
class, for instance:
// Using the setContainer() method(new Pipeline) ->setContainer(app()) // But, you can also pass the container instance in the class constructor(new Pipeline(app()))
Laravel pipelines are a powerful tool that allows developers to easily build and execute a sequence of operations in a clean and organized way. They are flexible and can be customized to fit any use case, making them a valuable addition to any Laravel project. By leveraging the pipeline pattern, developers can improve the scalability and maintainability of their code while also making it easier to implement complex and multi-step processes.
Laravel’s documentation on pipelines: https://laravel.com/docs/10.x/helpers#pipeline