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

Conveyor Belt Laravel Package

glhd/conveyor-belt

Conveyor Belt is a Laravel package for composing queued jobs into pipelines. Define ordered stages, run independent jobs in parallel, and orchestrate multi-step background workflows while relying on Laravel’s queue for retries and scaling.

View on GitHub
Deep Wiki
Context7

Getting Started

  • composer require glhd/conveyor-belt — no service provider registration needed on Laravel 9+ (package discovery), and no migrations or published assets are required to get a first belt running.
  • Read the README first, then look at src/Belts/ and the trait that mixes runConveyorBelt() into your command. Those two files are where 90% of the behaviour you'll rely on lives; the rest of the package is glue.
  • The first use case to wire up: an Artisan command that iterates a large Eloquent result set and shows a progress bar. Add the belt trait to the command, return the query (or iterable) from the belt's source method, put your per-row work in the processing method, and you get a formatted progress bar, elapsed time and ETA without writing ProgressBar boilerplate.
  • Run it against a small table first (--limit/where id < 100) so you can see the bar complete; then point it at production-scale data.
  • If you want to see exactly what the belt does per row (bar advance, rate limiting, exception wrapping), drop a dd() inside the processing callback — the belt is thin enough to trace with ray() or Xdebug without stepping through framework internals.

Implementation Patterns

  • One belt per command. Keep the source (query/iterable/Countable) and the row handler as separate small methods. That split makes the command testable and lets you swap the source for a fake in tests.

  • Countable totals (new in 2.4.0). The belt now accepts a Countable where it previously wanted an iterable, so the progress bar's max is derived from count() instead of being indeterminate or manually set. The idiomatic pattern is a small value object that is both iterable and countable:

    class PendingOrders implements IteratorAggregate, Countable
    {
        public function getIterator(): Traversable { return Order::wherePending()->lazyById(); }
        public function count(): int { return Order::wherePending()->count(); }
    }
    

    This is the cleanest way to get an accurate ETA on a query you don't want to hydrate up front.

  • Countable for non-query sources. Any domain collection that knows its own size (a Collection of file paths, an array of tenant IDs, a paginated API response with a total) can be passed straight in. Previously you had to count() separately and hand the number to the bar; now it flows through the belt.

  • Jobs instead of inline work. For work that must be retried, keep the belt as the driver and dispatch jobs from the row handler. The belt stays fast (it's just iterating and rendering), and the progress bar's cadence reflects enqueues rather than job completion — decide which you actually want to display.

  • Compose with existing Artisan affordances. Keep --dry-run, --chunk=, and --limit= as real options and thread them into the source, not the handler. The belt's bar will then show the honest total for the run you asked for.

  • Testing. Point the source at an in-memory collection and assert on side effects, not on output. If you need to assert the bar, Artisan::call() and check Artisan::output() contains the expected step count.

  • Scheduling. Wrapping the command in Schedule::command(...) works, but see the non-interactive gotcha below before you do.

Gotchas and Tips

  • count() and the iterator must agree. The bar's max is resolved once, from count(). If rows are inserted/deleted mid-run, or your count() applies different constraints than your iterator, the bar will finish early or blow past 100%. Keep both behind the same scope/builder method so they can't drift.
  • Resolve count() with a cheap query. A Countable whose count() does a select count(*) over a huge table is usually fine, but one that hydrates a collection just to call ->count() defeats the point. Use ->toBase()->count().
  • Countable does not mean bounded memory. Getting a total doesn't mean you can get() the rows. Keep lazyById()/cursor() inside getIterator(); the new feature only affects the total, not buffering.
  • Non-interactive output is noise. Under cron, CI, or a queued invocation, the progress bar writes escape codes into logs. Guard the belt or pass --quiet, and prefer $this->output->isDecorated() / $this->option('no-interaction') checks rather than hardcoding --no-ansi.
  • Don't mutate the cursor's key set. Deleting or re-keying rows while iterating a raw cursor() skips records. Use lazyById() or chunk by a stable key, and prefer soft deletes during the run.
  • Long rows stall the ETA. The rate estimate is a moving average; if one row takes 100× the others, the ETA lies for the rest of the run. That's cosmetic, but users will file bugs about it — consider logging per-row timing separately.
  • Nested belts render garbage. Two active progress bars on one output stream overwrite each other. If a row handler needs its own bar, buffer its output or run it in a sub-process (Process::run on another Artisan command) rather than nesting belts.
  • Exception behaviour. Decide explicitly whether a failing row should abort the run or be collected. Wrap the handler body in a try/catch that records the failure and continues when you want best-effort semantics, and let it bubble when you don't — the belt won't make that decision for you.
  • Extension point. The bar itself is built from the command's output; if you need custom columns (memory, rate, custom labels), format the bar once in a configureProgressBar()-style hook (or override the belt's formatting method) rather than re-implementing the loop.
  • Upgrade note. 2.4.0 is additive — the iterable signature still works, so existing belts need no changes. The only risk is if you previously passed an object that happened to implement Countable and relied on the belt ignoring it; the bar's total will now come from count() rather than being indeterminate.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager