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

Queue Bundle Laravel Package

kuborgh/queue-bundle

A Symfony bundle that adds queue-based background job processing to your application. It provides configuration, services, and console commands for publishing and consuming queued messages, helping you offload time-consuming tasks to workers with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is still not a Laravel package. kuborgh/queue-bundle is a Symfony bundle (registered via AppKernel/bundles.php, configured under a queue: key in config.yml). There is no service provider, no facade, no config/queue.php driver, and nothing that plugs into Laravel's Queue contract. It targets the PHP 5.x / Symfony 2–3 era and has zero dependents. You cannot composer require it into a Laravel app and expect dispatch() to work.

What v1.0.1 changes — and what it doesn't. The release is a patch, not a revival: a bugfix (#1, no detail in the notes), a Logentries logging integration, and getJobByCommand() for looking up the status of a time-critical job. None of that touches the PHP/Symfony version constraint, the service-provider gap, or the serialization format. The headline assessment stands. Two of the three items are still worth reading as patterns, covered below.

So "getting started" here means one of two things:

  1. Evaluating it as a spike (rare, and only if you're maintaining a legacy Symfony app that already depends on it):

    composer require kuborgh/queue-bundle
    

    Register the bundle in your kernel, add the minimal config, then look at Command/ in the vendor directory first — that's where the producer command (enqueue a Symfony command) and the worker command (consume and execute) live. That pair is the entire mental model: serialize a console command + its arguments into a store, have a long-running worker pop and run() it. With v1.0.1, add one more stop: wherever getJobByCommand() is defined, because that tells you how jobs are keyed and whether status is derivable from the store at all.

  2. Stealing the pattern for Laravel (the realistic path). The first use case it was built for — "this artisan/symfony command takes 90 seconds, don't block the request or the cron" — maps one-to-one onto:

    // app/Jobs/RunReportCommand.php
    class RunReportCommand implements ShouldQueue
    {
        use Dispatchable, InteractsWithQueue, Queueable;
    
        public function __construct(public array $args = []) {}
    
        public function handle(): void
        {
            Artisan::call('reports:generate', $this->args);
        }
    }
    
    RunReportCommand::dispatch(['--month' => '2026-01']);
    

    with php artisan queue:work as your worker. That is the package's value proposition, implemented natively, maintained, and with retries, backoff, failed-job storage, and Horizon metrics included.

Where to look in the repo: Command/ (producer + worker), the queue storage abstraction (DB table vs. file — this determines whether you get concurrency safety at all), the job-status lookup added in v1.0.1, and composer.json for the PHP/Symfony constraints before you waste an afternoon on a dependency conflict.

Implementation Patterns

Producer/consumer split. The bundle's model is: a web request (or cron) enqueues a command name plus argument array; a separate process runs a worker loop that deserializes and executes it. If you're porting this shape to Laravel, keep the split but replace the transport:

Bundle concept Laravel equivalent
Enqueue a Symfony command dispatch(new RunConsoleCommand($name, $args))
Worker loop process php artisan queue:work --queue=commands
Queue storage table/file QUEUE_CONNECTION=redis / database
Command arguments serialized Job constructor properties (serialized via SerializesModels)
Failed execution failed_jobs table + queue:failed / queue:retry
getJobByCommand() status check (v1.0.1) Job status column / Bus::batch() / cache progress keys
Logentries handler (v1.0.1) Monolog channel in config/logging.php

Dedicated queue for CLI work. If you mix "run an artisan command" jobs with fast jobs, a slow command hogs workers. Use a named queue and a dedicated worker:

public function viaQueue(): string { return 'commands'; }
php artisan queue:work --queue=commands --timeout=600 --tries=1

Status tracking, the one genuinely new idea in v1.0.1. getJobByCommand() answers "did my time-critical job finish?" by looking the job up from the command identity. That's a useful shape if you have a request that dispatches work and then needs to show progress. In Laravel, don't invent a lookup table — use the built-ins:

// Dispatch and remember the batch id
$batch = Bus::batch([new RunReportCommand($args)])->onQueue('commands')->dispatch();
Cache::put("report:{$id}:batch", $batch->id, now()->addDay());

// Poll
$batch = Bus::findBatch(Cache::get("report:{$id}:batch"));
return ['progress' => $batch->progress(), 'done' => $batch->finished()];

For single jobs where you just need a percentage, have handle() write progress into the cache keyed by a job UUID passed into the constructor. Either way, key on a value you control (UUID / batch id), never on a command string — command names collide across concurrent runs, which is exactly the limitation getJobByCommand() inherits.

Long-running command semantics. Symfony commands often aren't idempotent (they may half-write a report before dying). Pattern: wrap the Artisan::call in a job with $tries = 1 and an explicit failure handler that cleans up partial output, rather than relying on blind retries. If retries are safe, set $backoff and retryUntil().

Scheduling. With the bundle you'd cron the worker. In Laravel, keep schedule:run for dispatch and queue:work (or Horizon) for execution — never put slow work directly in Kernel::schedule():

$schedule->job(new RunReportCommand(['--month' => now()->subMonth()->format('Y-m')]))
         ->monthlyOn(1, '02:00')
         ->onOneServer();

Logging integration. v1.0.1 wires a Logentries handler so worker output ships off-box. The Laravel analogue is a named channel plus a stack, and it's worth doing for exactly the same reason — a crashed worker's stdout is the only diagnostic you get, and it dies with the container:

// config/logging.php
'queue' => [
    'driver' => 'monolog',
    'handler' => StreamHandler::class,
    'with' => ['stream' => storage_path('logs/queue.log')],
    'tap' => [App\Logging\AddJobContext::class],
],

Integration tip. If you genuinely must bridge to a legacy Symfony command living in the same codebase, don't resurrect this bundle — shell out via Symfony\Component\Process\Process from inside a Laravel job, or expose the legacy command behind an HTTP endpoint the job calls. You keep Laravel's retry/monitoring and avoid a 2016 serializer on PHP 8.

Gotchas and Tips

  • v1.0.1 is a nudge, not a revival. One unspecified bugfix, one logging handler, one accessor. The PHP 5.x / Symfony 2–3 baseline is untouched — assume no PHP 8 compatibility, no symfony/serializer 5+/6+ support, and no security patches. Treat it as reference reading, not a dependency. Do not read a patch release as evidence of a support commitment.
  • The Logentries feature points at a dead service. Logentries was folded into Rapid7 and the product was retired years ago; the integration is a Monolog handler for an endpoint that no longer accepts writes. Its presence in a new release is the clearest possible signal about how current this codebase is. Port the idea (ship worker logs off-box) to a maintained channel, not the handler itself.
  • getJobByCommand() is a polling API, so it inherits every polling flaw. Status is only as fresh as the store, there's no "unknown vs. failed vs. never-enqueued" distinction documented, and command-name-keyed lookup breaks with concurrent runs of the same command and different arguments. If you copy the pattern, key on a UUID and model at least three states.
  • Serialization is the failure point. Queues that serialize console command objects break the moment a class is renamed, a constructor arg changes, or a closure sneaks into the payload. If you build your own version, serialize primitives (command name + plain array of options), never objects.
  • Worker memory and code staleness. Any long-running worker — this one or queue:work — loads code once. After every deploy you must restart workers (php artisan queue:restart). Symptom: "my fix isn't live" on jobs but is live on web requests.
  • No visibility timeout / ack semantics. A simple DB-or-file queue typically lacks atomic claim + timeout, so a crashed worker leaves an item "in flight" forever or double-processes it. Redis (queue:connections.redis.retry_after) or SQS gives you this for free — another reason to port.
  • Debugging. For the bundle: tail the worker process output and inspect the backing table/file directly; there's no failed_jobs equivalent, so add logging at the top of your producer. The new status lookup helps confirm "did it run at all" but won't tell you why it failed. For Laravel: php artisan queue:failed, queue:retry all, and Horizon::routeMailNotificationsTo() / horizon:list when you need per-job timing.
  • Config quirks. Symfony bundle config is YAML under a bundle-specific root key and is validated at container compile time — a wrong key fails loudly, unlike Laravel's silently-defaulted config/queue.php. Note that the new Logentries wiring adds another key under that root; if you're copying config across, don't carry driver names or the logger block. sync, database, redis, sqs are the only real choices you need.
  • Extension points worth copying. The bundle's clean seam is the storage abstraction (swap file for DB). In Laravel the analogous seam is a custom Queue driver via Queue::extend() — useful if you have an internal message broker. Almost everything else in the bundle is now redundant with Bus, Horizon, failed_jobs, and batch progress.
  • Bottom line for a Laravel dev: spend ten minutes reading its producer/worker split — plus the new status-lookup and worker-logging bits, which are the two ideas with any transfer value — then implement them with ShouldQueue, Bus::batch(), and a dedicated commands queue. Do not add this to composer.json.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect