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

Pipeline Builder Laravel Package

solution/pipeline-builder

A Laravel package for composing reusable, testable pipelines from modular stages, with a fluent builder API for defining, running, and managing multi-step data processing workflows in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you install, verify it runs at all. This package's last release was April 2016, so it targets PHP 5.x/7.0 and the pre-ext-mongodb era. Spend ten minutes in a throwaway container before you plan anything around it:

composer require solution/pipeline-builder
php -v          # try 7.4 first; 8.x is likely to fatal on old syntax

If composer require resolves against a modern Laravel, great — but read composer.json in vendor/solution/pipeline-builder and note what illuminate/support constraint it dragged in. That constraint, not the package itself, is usually what breaks your app.

Registration. Registering looks like a Laravel 4/5-era package: add the service provider to config/app.php under providers and, if the README documents one, an alias under aliases. There is no extra.laravel block from 2016, so auto-discovery will not fire. Check src/ for *ServiceProvider.php and *Facade.php to confirm the exact class names rather than trusting the README.

Where to look first, in order:

  1. tests/ — if there are tests, they are the only reliable API reference for an unmaintained package. They show real, working call chains.
  2. src/ — the builder class itself. You are looking for one method per aggregation stage (match, group, project, sort, unwind, limit, skip) and one terminal method that yields the pipeline array.
  3. README.md — treat as aspirational; cross-check every example against src/.

First use case. The package's only real job is turning fluent PHP into a $pipeline array. So start with the smallest thing that proves that:

$pipeline = Pipeline::collection('orders')
    ->match(['status' => 'paid'])
    ->group(['_id' => '$customerId', 'total' => ['$sum' => '$amount']])
    ->sort(['total' => -1])
    ->toArray();   // ← confirm the real terminal method name in src/

// hand the array to whatever driver you already use
$collection->aggregate($pipeline);

If that round-trips, you have a working baseline. If it does not, you have saved yourself a week.

Implementation Patterns

Use it as an array builder, not as a data-access layer. The highest-value pattern is to treat this as a pure function from fluent calls to a pipeline array, and keep execution in your own repository classes. That means if you later replace it, you replace one line per query, not an entire data layer. Never let the facade leak into controllers.

Wrap it behind your own seam. Because the package is unmaintained, put a thin interface in front of it on day one:

interface PipelineCompiler
{
    public function compile(array $definition): array;
}

Your SolutionPipelineBuilder implementation is then swappable for a hand-rolled compiler or mongodb/laravel-mongodb's builder. This is cheap now and expensive later.

Compose reusable fragments, not whole pipelines. Most reporting queries share a base scope (tenant filter, date window, soft-delete exclusion). Build those as private methods that return a configured builder, then extend it per report:

private function baseScope(string $tenantId)
{
    return Pipeline::collection('events')
        ->match(['tenant' => $tenantId, 'deleted' => ['$ne' => true]]);
}

$pipeline = $this->baseScope($tenant)
    ->match(['createdAt' => ['$gte' => $from, '$lt' => $to]])
    ->group(['_id' => '$type', 'n' => ['$sum' => 1]])
    ->toArray();

Keep stage order explicit and visible in the caller — do not bury $sort before $match inside a helper, because ordering is semantically load-bearing in aggregation and reviewers need to see it.

Test the pipeline as data. This is where a fluent builder earns its keep: assert on the resulting array in PHPUnit, with no Mongo connection required.

$this->assertSame(
    [['$match' => ['status' => 'paid']], ['$limit' => 10]],
    (new MyReport)->pipeline()->toArray()
);

Snapshot assertions catch stage-order regressions that would otherwise only show up as silently wrong numbers in production.

Escape and type-convert deliberately. Whether the builder converts PHP DateTime to UTCDateTime and strings to ObjectId is the single most important thing to verify in src/. If it does not, do the conversion in your repository before handing values to the builder and never pass raw request input straight through.

Gotchas and Tips

GPL-2.0 is a real decision, not a footnote. This is copyleft. Linking it into a closed-source commercial product is a legal question, not an engineering one — and it is a question that gets far more expensive to answer after the code ships. If your product is proprietary, escalate before you write the first line. A ~200-line internal builder is almost certainly cheaper than the review.

Feature coverage stops at MongoDB ~3.2. Released in 2016, it cannot know about $facet, $bucket, $graphLookup, $merge/$out, $replaceRoot, $addFields, the pipeline form of $lookup, or $$NOW/$$ROOT variable expressions. If you need any of those — and most modern reporting work does — you will be dropping to raw arrays anyway, at which point the abstraction is paying rent on half your queries.

Driver-era mismatch. It likely speaks the legacy MongoId/MongoDate vocabulary or an early ext-mongodb. Passing its output to a current mongodb/mongodb library can produce documents that serialize but do not query correctly. Always json_encode($pipeline) and eyeball the types before sending.

Debug by dumping the array, always. When a pipeline returns zero rows, the fluent chain gives you nothing to inspect. Print the compiled array, pretty-print it next to the equivalent hand-written pipeline, and diff. Nine times out of ten the bug is a stage in the wrong position or a $ prefix the builder added (or failed to add) for you.

Check for Laravel Macroable. If the builder class uses Macroable, you have a clean extension point: register macros for the newer stages ($facet, $addFields) in a service provider instead of forking. That keeps you on the released package while filling its gaps. If it does not, the extension point is subclassing — check that stage methods are protected/public and not private, otherwise you are forking.

Do not fork casually. A fork inherits the GPL obligation and the maintenance burden. If you fork, do it to add stages, not to modernize PHP syntax — the latter is a rewrite in disguise.

Honest recommendation. With 5 stars, zero dependents, and eight years of silence, there is no community to help you when it breaks and no path to a security fix. For a new project, write the pipeline arrays directly or use a maintained ODM; MongoDB's aggregation syntax is already close enough to a DSL that a builder saves less than it appears to. Adopt this package only for a genuinely short-lived internal tool, or to read code someone else already wrote — and in the latter case, budget for replacing it rather than extending it.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core