Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Piper Laravel Package

transprime-research/piper

View on GitHub
Deep Wiki
Context7

Getting Started

Piper gives you a fluent, class-based pipeline: you push a payload through an ordered set of single-purpose classes ("pipes"/"links") and get a result back. In 3.0.0 the package also supports a proxied entry point, so you can talk to the chain through one object instead of wiring ->through([...])->then(...) at every call site.

Minimal setup:

composer require transprime-research/piper:^3.0

3.0.0 sets the minimum PHP version to 7.4. If you are still on 7.2/7.3 anywhere (local Docker image, CI matrix, a legacy service), you cannot install this version — check composer.json / your CI image before bumping.

First steps:

  1. Confirm package discovery picked it up — php artisan about or simply resolve the class from the container (app(\Transprime\Piper\Piper::class)).
  2. Read src/ first: the Piper class (the builder/proxy surface) and Link.php (the individual step). Those two files define the whole mental model; the README examples will make sense once you've skimmed them.
  3. Check composer.json for the supported Laravel/illuminate range — 3.0 is a major bump, so don't assume the 2.2 constraint still holds.
  4. Look at tests/ for the canonical usage shape before inventing your own.

First realistic use case: a fat controller action that validates, creates a model, dispatches side effects, and returns a response. Split it into pipes and let Piper own the sequencing:

use Transprime\Piper\Facades\Piper;

return Piper::make($request->validated())
    ->through([
        ResolveCustomer::class,
        CreateOrder::class,
        ApplyDiscounts::class,
        ChargeCard::class,
        SendReceipt::class,
    ])
    ->then(fn (Order $order) => OrderResource::make($order));

Each pipe is a plain class with a handle($payload, $next) (or __invoke) method and gets constructor-injected dependencies from the container — no new, no manual wiring.

Implementation Patterns

1. Pipes as single-responsibility classes. The value of the package is only realised if each link is small and testable. Pass a DTO, not $request->all():

final class ApplyDiscounts
{
    public function __construct(private DiscountRepository $discounts) {}

    public function handle(OrderPayload $payload, \Closure $next): mixed
    {
        $payload->order->apply($this->discounts->for($payload->customer));

        return $next($payload);
    }
}

2. Proxied calls (new in 3.0). Instead of repeating the chain definition, bind the configured piper once and call methods "through" it — the proxy resolves the target and passes the result down the configured links:

// AppServiceProvider
$this->app->bind('order.piper', fn ($app) => $app->make(Piper::class)
    ->through([ValidateOrder::class, PersistOrder::class, NotifyCustomer::class]));

// Anywhere
app('order.piper')->place($dto);

Treat this as the "facade for a workflow" pattern: one named, reusable chain per domain action. Verify the exact proxy semantics against src/ on 3.0 — PR #9 ("Proxied piper call") landed right before the release, and PR #13 touched Link.php, so the call surface and the link contract are the two things most likely to differ from 2.x examples you'll find in blogs.

3. Reusable chains via Link. If you need the same sub-sequence in several workflows (e.g. "authorise → reserve stock"), model it as a Link and compose it, rather than copy-pasting the classes into every through([...]). This keeps ordering in one place.

4. Short-circuiting. Return early from a pipe when there's nothing left to do, or throw a domain exception and let the global handler / a try-catch at the call site translate it into a response. Don't return false and rely on truthiness — the payload is the contract.

5. Transactions at the boundary, not inside pipes. A chain that writes to several tables should be wrapped by the caller:

$order = DB::transaction(fn () => Piper::make($dto)->through([...])->then(fn ($o) => $o));

Pipes stay unaware of persistence boundaries, so they remain reusable in jobs and tests.

6. Console / queue contexts. The same chain runs from a queued job or an artisan command — that's the main payoff over a controller-shaped service class. Keep pipes stateless so a job retry re-runs cleanly.

Gotchas and Tips

  • Treat 3.0.0 as a real major. The released notes only call out the PHP 7.4 floor and CI changes, but PR #13 rewrote Link.php and PR #9 added proxying. Read the diff (compare/2.2.0...3.0.0) before upgrading — for a small package it's a ten-minute read and it's the only reliable changelog you'll get.
  • Pin in CI. If your pipeline runner still uses PHP 7.2/7.3 images, composer update will fail rather than silently downgrade. Add the version constraint check to your Dockerfile/CI matrix in the same PR as the upgrade.
  • Ordering is the program. The majority of bugs in pipeline code are "wrong class at wrong index". Assert the order in a test (->through([...]) snapshot) if the sequence encodes business rules.
  • Don't dd() into a proxy chain from a queue. Log at the top of each pipe with the payload's identifying field instead; a var_dump inside a proxied call gives you the proxy, not the resolved target — resolve app(YourPipe::class) directly when you need to inspect behaviour.
  • Container resolution failures look like pipeline failures. A BindingResolutionException mid-chain usually means a pipe's constructor type-hint isn't bound, not that Piper is broken. app(ThatPipe::class) in tinker isolates it in seconds.
  • Test pipes in isolation. Invoke handle($payload, fn ($p) => $p) with a recording closure — no Piper instance needed, no Laravel app needed for the pure ones.
  • Extension points worth knowing: bind your own implementation against the package's pipe/link contract if you need cross-cutting behaviour (timing, tracing) without touching every class; and prefer decorating a Link over adding conditionals inside pipes.
  • Ecosystem risk. This is a small, single-maintainer package with a thin changelog. It's a great fit for internal workflow sequencing; be cautious about making it load-bearing in a package you distribute, and keep the chain definitions in your own code (not scattered in vendor config) so a future major is a contained edit.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment