Laravel Pipelines

Laravel Pipelines
Photo by Helio Dilolwa / Unsplash

In Laravel, the Pipeline class offers a streamlined approach to process data through a series of tasks, or “pipes,” in a sequential manner. This design promotes clean, maintainable, and testable code by isolating each step of a process into its own class.

Understanding the Pipeline Concept

A pipeline allows you to pass data through multiple stages, where each stage performs a specific operation on the data before passing it to the next. This is particularly useful for scenarios like:

  • Filtering or transforming data.
  • Applying a series of business rules.
  • Processing user input through various validation or transformation steps.

Implementing a Pipeline in Laravel

Let’s walk through an example to illustrate how to implement a pipeline. Suppose we have a scenario where we need to process a webshop checkout by performing the following tasks:

Calculate tax: Get the right amount of tax in each country.

Apply coupons: Subtract coupon value (if apply).

Add Shipping costs: Add the amount of shipping costs based on overall price and country.

Step 1: Define the Pipe Classes

Each pipe will be a class responsible for a single task.

namespace App\Pipes;

use Closure;
use App\Models\Order;

class CalculateTax
{
    protected $taxes = [
      'Germany' => 0.19,
      'Italy' => 0.20,
      'France' => 0.21,
    ];

    public function handle(Order $order, Closure $next)
    {
        $order->tax = $this->taxes[$order->shipping->country];
        $order->amount += $order->amount * $order->tax;

        return $next($content);
    }
}
namespace App\Pipes;

use Closure;
use App\Models\Order;
use App\Models\Coupon;

class ApplyCoupon
{
    public function handle(Order $order, Closure $next)
    {
        $couponValue = Coupon::where('code', $order->couponCode)->first()->value;
        $order->amount -= $couponValue;
        
        return $next($content);
    }
}
namespace App\Pipes;

use Closure;
use App\Models\Order;

class AddShippingCosts
{
    protected $shippingCosts = [
      'Germany' => 5.50,
      'Italy' => 7.90,
      'France' => 8.10,
    ];

    public function handle(Order $order, Closure $next)
    {
        $order->shipping->costs = $this->shippingCosts[$order->shipping->country];
        $order->amount += $order->amount * $order->shipping->costs;

        return $next($content);
    }
}

Step 2: Configure the Pipeline

In the part of your application where you need to process the comment, set up the pipeline:

use Illuminate\Pipeline\Pipeline;
use App\Pipes\CalculateTax;
use App\Pipes\ApplyCoupon;
use App\Pipes\AddShippingCosts;

$processedContent = app(Pipeline::class)
    ->send($comment)
    ->through([
        CalculateTax::class,
        ApplyCoupon::class,
        AddShippingCosts::class,
    ])
    ->thenReturn();

In this setup:

send($order) Sends the initial comment into the pipeline.

through([...]) Specifies the list of pipes the comment should pass through.

thenReturn() Executes the pipeline and returns the processed content.

Optionally you can use

then() instead of thenReturn to apply some final logic like this:

use Illuminate\Pipeline\Pipeline;
use App\Pipes\CalculateTax;
use App\Pipes\ApplyCoupon;
use App\Pipes\AddShippingCosts;

$processedContent = app(Pipeline::class)
    ->send($comment)
    ->through([
        CalculateTax::class,
        ApplyCoupon::class,
        AddShippingCosts::class,
    ])
    ->then(function ($finalContent) {
        return strtoupper($finalContent);
    });

Benefits of Using Pipelines

  • Separation of Concerns: Each pipe handles a specific task, making the code modular and easier to manage.
  • Reusability: Pipes can be reused in different pipelines or contexts.
  • Testability: Individual pipes can be tested in isolation, ensuring each step works correctly.

Conclusion

Laravel’s Pipeline provides a powerful mechanism to process data through a series of steps, promoting clean and maintainable code. By structuring your processes into discrete pipes, you can enhance the readability and flexibility of your application.