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 Manager Bundle Laravel Package

mcfedr/queue-manager-bundle

A Symfony bundle for managing queue jobs, giving you a web interface and commands to inspect, retry, and delete failed or pending messages across your queues.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a stack check. mcfedr/queue-manager-bundle is a Symfony bundle: it ships a Bundle class, Doctrine entities, DI service definitions, and console commands. It is not a Laravel package — there is no service provider, no config/queue.php driver, and no Horizon integration. In a pure Laravel app you cannot just composer require it and have php artisan queue:work pick up jobs. Decide which mode you are in before writing any code:

  1. Symfony host you own (a sidecar service, a monorepo service, or a legacy app you maintain) — use the bundle directly and call into it from Laravel over HTTP or a shared broker.
  2. Laravel-only — treat the bundle as a reference architecture and port the pattern (DB-backed job records + queue containing job IDs).
  3. Laravel front-end, Symfony worker — the realistic integration: Laravel enqueues work, a Symfony process running this bundle consumes it.

Minimal steps in a Symfony host:

composer require mcfedr/queue-manager-bundle
  • Register Mcfedr\QueueManagerBundle\McfedrQueueManagerBundle in config/bundles.php.
  • Configure a driver (AMQP/SQS/Doctrine depending on version) and a handler per queue in config/packages/mcfedr_queue_manager.yaml.
  • Run the Doctrine schema update so the job table exists.
  • Implement the bundle's handler interface for one queue.
  • Start the bundle's worker console command under supervisord/systemd.

Where to look first: the bundle README's config reference, the JobManager service (the dispatch entry point), the Job entity (status/params/result/timestamps), the handler interface, and the worker command. If Laravel needs to inspect job state, also look at mcfedr/queue-manager-rest-bundle — an HTTP surface is usually a cleaner contract than sharing a database.

First use case: a slow operation you don't want in a Laravel request — image derivatives, PDF/report export, third-party sync. Laravel records intent (a local remote_jobs row with the returned job id), the Symfony worker executes it, and Laravel reconciles status.

// Symfony side: one handler per queue
final class ThumbnailHandler implements JobHandler
{
    public function handle(Job $job): void
    {
        $params = $job->getParams(); // keep these scalar: ids, paths
        // ... do the work, idempotently
    }
}
// Laravel side: wrap the integration in a client + queued job
final class DispatchRemoteJob implements ShouldQueue
{
    public function handle(RemoteQueueClient $client): void
    {
        $jobId = $client->create('thumbnails', ['image_id' => $this->imageId]);

        RemoteJob::create([
            'remote_id' => $jobId,
            'queue' => 'thumbnails',
            'status' => 'pending',
        ]);
    }
}

Implementation Patterns

Job id in the message, state in the DB. The bundle's model is the one worth copying even if you never install it: the broker message is a pointer (job id/queue), and the row holds params, status, start/finish times, and result. That gives you visibility, reruns, and debugging that a raw serialized payload doesn't. Mirror this in Laravel with a remote_jobs table rather than stuffing payloads into the broker.

Handler-per-queue mapping. Keep one handler service per queue name and keep the mapping explicit in config. Do not multiplex by params (if ($params['type'] === ...)) — it makes routing opaque and reruns ambiguous.

Three integration shapes, in order of preference:

  1. REST bundle — Laravel gets a typed client (RemoteQueueClient) with create/status/cancel. Version the payload, sign requests, retry with backoff. Cleanest boundary; no shared schema.
  2. Shared broker — Laravel publishes the same message contract the Symfony worker expects (job id + queue). Works well, but you now own a cross-language contract: pin the shape, add a contract test on both sides, and treat a shape change as a breaking change.
  3. Shared database — Laravel inserts rows the worker picks up. Fastest to prototype, worst to own: two frameworks migrating one table. Use only inside one team with a dedicated schema and a single owner of migrations.

Worker deployment. Treat the Symfony worker like a Laravel queue worker: one long-running process per queue or driver, supervised, restarted on deploy, logs shipped to the same place as your Laravel logs. If the command supports a max-jobs/time-limit option, use it so workers recycle — long-running PHP processes leak memory.

Status reconciliation. Add a scheduled Laravel command that polls non-terminal remote_jobs and updates local state, with jitter and backoff. Don't poll in the request cycle. If the bundle supports callbacks/webhooks, prefer them and keep polling as a safety net.

Porting the pattern to Laravel. If the goal is the behavior rather than the package: RemoteJob model, a DispatchRemoteJob queued job, a worker command that claims rows with SELECT ... FOR UPDATE SKIP LOCKED, idempotent handlers keyed by queue name, and failed_jobs-style dead-letter visibility. That is a weekend of work and removes the cross-framework dependency.

Local development. Run the Symfony worker as a container alongside Laravel in docker-compose, and use the Doctrine driver locally for zero broker infrastructure, then switch to AMQP/SQS in staging. Keep the driver choice in env, not in committed config.

Gotchas and Tips

  • No php artisan surface. queue:work, queue:retry, queue:failed, and Horizon will not see these jobs. Your operational runbooks need Symfony-side equivalents or a wrapper.
  • Doctrine/Eloquent schema ownership. Sharing the job table means Laravel migrations and Doctrine schema tooling both think they own it. Pick one migrator; make the other read-only. Prefer REST to avoid this entirely.
  • Two sources of truth. A row can be committed while the publish fails, or a message can arrive for a row that was rolled back. Check what the bundle does around transaction/publish ordering for your pinned version; if it is naive, add a reconciliation sweep for orphaned rows and unknown job ids.
  • Idempotency is mandatory. Rerun/retry commands will re-execute handlers. Guard side effects with a dedupe key (job id works) and make writes upserts where possible.
  • Keep params small and scalar. Params are persisted for the life of the job; don't put Eloquent models, base64 blobs, or large documents in them. Pass ids and let the handler load data.
  • Exact-match config. Driver type strings and queue-name-to-handler keys must match the worker's expectations character for character. Clear the Symfony cache after config changes; a stale container is the usual cause of "my new handler isn't called."
  • Pin and read the source. Last release 2024-09-25, ~10 stars, no dependents. That is vendor risk. Pin an exact version, read the command names and config keys from the code you installed rather than from blog posts, and put an integration test around the job contract so a minor upgrade cannot silently change it.
  • Failure visibility. The bundle tracks status in the DB, but queue depth and dead-lettering live in the broker. Alert on both: rows stuck in a non-terminal status past an SLO, and broker queue depth. Laravel's failed_jobs table gives you none of this.
  • Extension points are also breakage points. Custom drivers, custom job entities/repositories, and handler tags are where upgrades hurt. If you must extend, isolate it behind your own adapter class rather than wiring bundle internals into application code.
  • Deploy ordering. Restart workers after code deploys, and deploy handler changes before dispatching new param shapes. A worker running old code against a new payload is the most common production incident with cross-service queues.
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