Use Laravel's Pipeline facade to pass objects through sequential pipe classes, keeping complex order processing and data transformations modular.
When an application workflow involves multiple distinct stages (such as validating payment, applying coupons, calculating tax, and generating invoices), stuffing all logic into a single service method creates monolithic, hard-to-test code.
Laravel's Pipeline pattern executes sequential pipes cleanly.
Defining Pipe Classes
Each pipe receives the payload and a $next closure:
namespace App\Pipelines\Orders;
use Closure;
class ApplyDiscountCoupon
{
public function handle(Order $order, Closure $next)
{
if ($order->coupon_code) {
$order->discount = CouponService::calculate($order);
}
return $next($order);
}
}
Executing the Pipeline
use App\Pipelines\Orders\ApplyDiscountCoupon;
use App\Pipelines\Orders\CalculateTax;
use App\Pipelines\Orders\ProcessStripePayment;
use Illuminate\Support\Facades\Pipeline;
$processedOrder = Pipeline::send($order)
->through([
ApplyDiscountCoupon::class,
CalculateTax::class,
ProcessStripePayment::class,
])
->then(fn ($order) => $order->finalize());
Summary
- Deconstructs complex operations into focused, single-responsibility pipe classes.
- Enables easy reordering, adding, or removing of business steps.
- Uses standard middleware-style
handle($passable, Closure $next)signatures.
Related Tips
View all tips →Add Fluent when() and unless() Logic to Custom Classes with Conditionable
Use the Conditionable trait to equip your custom service classes, API clients, and query builders with fluent when() and unless() branching.
Keep Controllers Lean by Delegating Business Logic to Action Classes
Refactor fat controller methods by moving multi-step transactions, file storage, and third-party integrations into invokable Action classes.