aescarcha/async
Symfony bundle that listens to Doctrine entity lifecycle events (persist/update/remove) and publishes RabbitMQ jobs so heavy processing runs asynchronously. Configure via OldSoundRabbitMq and register the persist listener as consumer/event subscriber.
Install the Package
composer require aescarcha/async
Configure RabbitMQ
Update config/packages/old_sound_rabbit_mq.yaml (or config.yml in older Symfony versions) with your RMQ connection details:
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
producers:
async_refresher:
connection: default
exchange_options: {name: 'async-refresher', type: direct}
consumers:
async_refresher:
connection: default
exchange_options: {name: 'async-refresher', type: direct}
queue_options: {name: 'async-refresher'}
callback: aescarcha.persist_listener
Register the Listener
Add the service definition to config/services.yaml:
services:
aescarcha.persist_listener:
class: Aescarcha\AsyncBundle\Listener\PersistListener
arguments: ['@serializer', '@doctrine.orm.entity_manager']
tags:
- { name: kernel.event_listener, event: 'prePersist', method: 'onPrePersist' }
- { name: kernel.event_listener, event: 'preUpdate', method: 'onPreUpdate' }
- { name: kernel.event_listener, event: 'preRemove', method: 'onPreRemove' }
First Use Case
Trigger async operations on entity events (e.g., prePersist). Example:
// In your entity or controller
$entity = new YourEntity();
$entityManager->persist($entity);
$entityManager->flush(); // Async job is queued here
Event-Driven Queueing
prePersist, preUpdate, preRemove).public function onPrePersist(EntityManagerInterface $em, Entity $entity)
{
$job = new AsyncJob($entity, 'persist');
$this->asyncProducer->publish($job);
}
Consumer Processing
async_refresher consumer processes queued jobs:
// In AsyncBundle's PersistListener
public function onMessage(AsyncJob $job)
{
switch ($job->getAction()) {
case 'persist':
$this->entityManager->persist($job->getEntity());
break;
case 'remove':
$this->entityManager->remove($job->getEntity());
break;
}
$this->entityManager->flush();
}
Serialization Layer
Serializer to handle entity serialization/deserialization:
# services.yaml
Aescarcha\AsyncBundle\Serializer\EntitySerializer:
arguments: ['@serializer']
Integration with Doctrine
Doctrine\ORM\Event\LifecycleEventArgs to access the entity manager:
public function onPrePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
// Queue async job
}
AsyncJob to support additional actions (e.g., reindex, notify).RabbitMQ Connection Issues
old_sound_rabbit_mq configuration (host, credentials, vhost). Enable lazy: true for connection pooling:
connections:
default:
lazy: true
Entity Serialization Failures
SerializationException.Serializable or configure the Serializer to handle them:
# config/packages/serializer.yaml
framework:
serializer:
mapping:
paths: ['%kernel.project_dir%/config/serializer']
Create a mapping file (e.g., config/serializer/YourEntity.yaml):
Your\App\Entity\YourEntity:
attributes:
id: ~
name: ~
Consumer Not Processing Messages
php bin/console rabbitmq:consumer async_refresher
Ensure the callback in consumers matches the service ID (aescarcha.persist_listener).Transaction Management
flush() explicitly in the consumer to persist changes.old_sound_rabbit_mq:
connections:
default:
log_level: debug
# docker-compose.yml
services:
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
Custom Job Types
Extend AsyncJob to support domain-specific actions:
class ReindexJob extends AsyncJob
{
public function __construct(Entity $entity, string $indexName)
{
parent::__construct($entity, 'reindex');
$this->indexName = $indexName;
}
}
Dynamic Queue Routing Use exchange routing keys to direct jobs to different consumers:
producers:
async_refresher:
exchange_options: {name: 'async-refresher', type: direct}
routing_key: 'entity.{entity_class}'
Priority Queues Configure multiple queues with priorities:
consumers:
async_high_priority:
queue_options: {name: 'async-high-priority', priority: 1}
How can I help you explore Laravel packages today?