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

Async Event Dispatcher Bundle Laravel Package

tripcloud/async-event-dispatcher-bundle

Symfony bundle for asynchronous event dispatching, letting applications publish events without blocking the request. Integrates with Symfony's dispatcher to queue and process listeners in the background, improving response times and decoupling event handling in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony 2 bundle (not Laravel), hosted on Bitbucket, with zero stars, zero dependents, and no Composer/Packagist presence you can rely on. Before writing any code, confirm that Symfony 2 is still acceptable in your project — Symfony 2 reached end-of-life in 2018. If you are on Symfony 3+/4+/5+, you will be fighting the bundle's kernel registration and service definitions rather than gaining anything.

Minimal steps to exercise it:

  1. Pull it in via a VCS repository. Because it is Bitbucket-hosted and not on Packagist, add an explicit repositories entry to your composer.json pointing at https://bitbucket.org/tripcloud/asynceventdispatcherbundle, then composer require tripcloud/async-event-dispatcher-bundle. Expect to need Bitbucket credentials (app password / SSH key) if the repo is private.
  2. Register the bundle in app/AppKernel.php inside registerBundles() (e.g. new TripCloud\AsyncEventDispatcherBundle\TripCloudAsyncEventDispatcherBundle()), matching whatever the namespace actually is once you inspect composer.json / the bundle class.
  3. Configure the RabbitMQ connection in app/config/config.yml. Look for the bundle's DependencyInjection/Configuration.php to confirm the exact key names — typically something like tripcloud_async_event_dispatcher: { connection: { host, port, user, password, vhost }, exchange, queue }. There is no public docs site; the Configuration class is the documentation.
  4. Ensure the underlying AMQP transport is installed. This bundle almost certainly wraps php-amqplib (php-amqplib/php-amqplib) or oldsound/rabbitmq-bundle. Check composer.json for the transport dependency; if it is not pulled automatically, add it.
  5. Run a consumer worker. Symfony 2 async bundles typically expose a console command like app/console rabbitmq:consumer <name> or a bundle-specific command. Grep Command/ in the bundle for the actual command name.

First use case: pick the single slowest, non-critical side effect in your app — sending a transactional email, resizing an uploaded image, writing a search-index document, firing a webhook — and move only that behind the async dispatcher. Do not start by moving core domain events; get one low-risk path working end-to-end (produce → broker → consume → listener runs) before broadening.

Implementation Patterns

Producer / consumer split is the whole architecture. The bundle's job is to take an in-process Symfony event, serialize it to a message, publish to RabbitMQ, and (in a separate worker process) deserialize and dispatch it back to local listeners. Your day-to-day mental model:

  • Producer side (web request): your controller/service calls $dispatcher->dispatch('app.order.placed', $event). With this bundle wired in, the name is published as a message instead of (or in addition to) being handled inline. Keep the request path fast: the only work the HTTP request should do is serialize and publish.
  • Consumer side (worker): a long-running CLI process subscribes to the queue, deserializes the message, and re-dispatches to your ordinary Symfony event listeners. Register listeners the normal Symfony way (kernel.event_listener / kernel.event_subscriber) on the same event name.
  • Event payloads are DTOs, not entities. Define a small, explicit event class (OrderPlacedEvent with orderId, customerId, placedAt, total) — never pass a Doctrine entity, a repository, or a service. Only scalars/arrays/DateTimeImmutable survive the wire.
  • Decorator the synchronous dispatcher. The cleanest integration is a decorator around the framework event_dispatcher service: sync listeners keep working, async listeners are opted-in per event name (via config like async_events: [app.order.placed]). This lets you migrate event-by-event rather than flipping the whole dispatcher.
  • Fan-out via the exchange. Use a topic or fanout exchange so multiple consumers (email worker, analytics worker, search-index worker) each get a copy. The bundle will have an exchange/queue config block — model your topology explicitly there instead of relying on defaults.
  • Idempotent listeners are mandatory. At-least-once delivery means a listener can run twice after a consumer crash or ack timeout. Guard every listener: check processed_events / use a unique constraint / check the entity state before applying.
  • Operational plumbing around the worker: run the consumer under supervisor or systemd with auto-restart, one process per queue, --memory-limit and --sleep style flags if the command supports them (Symfony 2 daemons leak). Deploy = restart workers, not just composer install.

Workflow tip: treat the RabbitMQ Management UI (http://host:15672) as your primary integration test surface. Publish a message by hand into the queue and confirm the listener fires; once that works, wire up the producer. This isolates producer bugs from consumer bugs.

Gotchas and Tips

  • Symfony 2 is EOL and this bundle is effectively unmaintained. Zero stars, zero dependents, Bitbucket-hosted. That means no security patches, no PHP 8 support, no Symfony 4+ Flex integration, and no community to answer "why did my consumer die." Budget for vendoring/forking the bundle into your own repo rather than a hard Composer dependency, so you can patch it.
  • Serialization is the #1 pitfall. If your event listener type-hints a Doctrine entity or a service, deserialization on the consumer side will fail with confusing "could not instantiate" or "unserialize(): Error at offset" errors. Pass IDs and primitive fields; re-fetch the entity inside the listener. If the bundle uses PHP serialize() rather than JSON, a change to your event class (renamed property, added required arg) will break in-flight messages after deploy — drain the queue before deploying producer/consumer changes together, or version your event classes.
  • "My listener never fires." Walk this list in order: (1) is the consumer worker actually running (ps aux | grep console, supervisor status)? (2) is the event actually being published — check the exchange's message rate in the RabbitMQ UI and whether the queue has a binding? (3) is the routing key / binding key mismatched? (4) is the listener registered on the consumer's container (same kernel.event_listener tag)? A common failure is registering the listener only in the web container and not the CLI container.
  • "My listener fires twice." Delivery is at-least-once. Either the bundle acks after your listener runs (safe but replay-able on crash) or before (fast but can drop). You cannot fix this at the broker level — make the listener idempotent. Log a message_id and dedupe on it.
  • Config quirks. Symfony 2 bundles often ship a hardcoded default connection name that collides with oldsound/rabbitmq-bundle if both are installed. Check Configuration.php for the connection name it registers and the service IDs it wires. Also verify vhost permissions on the RabbitMQ user — composer require succeeding tells you nothing about configure/write/read ACLs.
  • No dead-letter queue out of the box. If the bundle doesn't configure a DLQ, a poison message will either loop forever (if it nacks/requeues) or silently vanish (if it acks on failure). Add a DLX/DLQ in the exchange arguments yourself, or at minimum log listener exceptions and set prefetch_count/qos so one bad message doesn't starve the worker.
  • Debugging loop: consumer output goes to stdout, so tail -f the supervisor log. RabbitMQ Management UI shows message rates, queue depth, and unacked counts — a rising queue depth means your consumer is down or too slow; a rising unacked count means a listener is hanging. rabbitmqctl list_queues name messages messages_unacknowledged consumers is faster than the UI once you know the queue name.
  • Extension points are the only reason to keep it. The bundle's value is the dispatcher decorator and the consumer command — both are small. If you inherit it, the pragmatic move is to copy those two classes into your own codebase (a Messenger-style wrapper is 200 lines), delete the dependency, and control the transport config directly. That gives you the async behavior without owning an unmaintained Bitbucket dependency.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
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
cgv/localized-resource