chefhasteeth/pipeline
A Laravel package for building and running pipelines of reusable stages. Pass a payload through each step, compose stages into classes, and use conditions and dependency injection to keep complex workflows clean and testable.
This package wraps Laravel’s pipe-and-filter pattern into a friendlier, container-aware Pipeline class. Install it via Composer and start by looking at the package’s Pipeline class and Pipe contract — those are the two things you’ll interact with most:
composer require chefhasteeth/pipeline
The first, simplest use case is passing a payload through a list of pipe classes:
use Chefhasteeth\Pipeline\Pipeline;
$result = Pipeline::make()
->send($payload)
->through([
\App\Pipes\ValidatePayload::class,
\App\Pipes\EnrichPayload::class,
])
->then(fn ($payload) => $payload);
Each pipe is a normal class resolved from the container with a handle($payload, Closure $next) method:
namespace App\Pipes;
use Closure;
class ValidatePayload
{
public function handle($payload, Closure $next)
{
// throw, mutate, or short-circuit
return $next($payload);
}
}
If you’ve used Illuminate\Pipeline\Pipeline before, this should feel familiar — the main difference is the static make() entry point and cleaner ergonomics out of the box.
Feature-style composition. Define one small class per step (App\Pipes\…) and compose them in a controller, job, or action class. This keeps handle chains shallow and makes each step independently testable.
$order = Pipeline::make()
->send($order)
->through([
EnsureStockAvailable::class,
CalculateTaxes::class,
ChargeCustomer::class,
SendReceipt::class,
])
->then(fn (Order $order) => $order);
Short-circuiting. Pipes can return early instead of calling $next when a precondition fails — a clean way to branch without deeply nested if statements. Throw domain exceptions in place of calling $next to fail fast.
Reusable pipelines as first-class objects. Wrap a fixed list of pipes in a dedicated class or service (OrderPipeline) so tests and controllers don’t repeat the array. This is also the easiest extension point if you later need to swap steps based on config or feature flags.
Dependency injection in pipes. Because pipes are resolved through the container, you can inject services (repositories, HTTP clients, loggers) into the constructor and use them inside handle. Keep the payload immutable where possible, but do pass state forward through $next($payload) — Laravel pipelines usually mutate one canonical object rather than returning new ones.
Integration with form requests and jobs. A common pattern is to build the pipeline inside a form request’s passedValidation() or inside a queued job’s handle(). Both give you a clean payload and clear boundaries around the pipe chain.
Testing. Assert each pipe in isolation by passing a fn ($p) => $p terminal closure. For the whole pipeline, assert on the final payload and mock only the boundaries (HTTP, DB writes).
$result = (new ValidatePayload)->handle($payload, fn ($p) => $p);
$next($payload), not $next. Every pipe must call the closure with the payload — forgetting the argument is the most common bug and silently drops your data.then() is required. There’s no implicit execution; if you forget the terminal callback, nothing runs. Use thenReturn() if available, or then(fn ($p) => $p).try block in a prior pipe will bubble straight out of then(). Wrap the whole pipeline at the call site if you need partial-failure handling, or catch inside a dedicated “wrap” pipe at the start.logger()->info() inside each handle or use ray() if installed — pipeline steps are otherwise opaque. Because they’re container-resolved, constructor errors will show up as resolution failures rather than pipeline errors.app()->bind() can cause pipes to receive a different implementation in tests than in production. Prefer interfaces bound in a service provider and swap the binding in tests.handle vs __invoke), check the package’s Pipe contract — subclassing or adapting it is the cleanest way to swap conventions without patching pipes individually.How can I help you explore Laravel packages today?