ecentria/ecentria-apievents-bundle
Installation Add the bundle via Composer:
composer require ecentria/ecentria-apievents-bundle
Enable it in config/bundles.php:
return [
// ...
Ecentria\APIEventsBundle\EcentriaAPIEventsBundle::class => ['all' => true],
];
Configuration Publish the default config:
php artisan config:dump
Update config/packages/ecentria_api_events.yaml with your RabbitMQ connection details:
ecentria_api_events:
rabbitmq:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
exchange: 'api_events'
queue: 'api_events_queue'
First Use Case: Listening to Events Create a Symfony event listener to handle domain events:
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Ecentria\APIEventsBundle\Event\DomainEvent;
class MyEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
DomainEvent::class => 'onDomainEvent',
];
}
public function onDomainEvent(DomainEvent $event)
{
// Handle the event payload
$data = $event->getData();
// ...
}
}
Run the Consumer Start the RabbitMQ consumer in a separate terminal:
php bin/console ecentria:api-events:consume
Publishing Events
Use the bundle’s EventPublisher service to dispatch domain events to RabbitMQ:
use Ecentria\APIEventsBundle\Service\EventPublisher;
class MyService
{
public function __construct(private EventPublisher $publisher) {}
public function triggerEvent()
{
$this->publisher->publish('user.created', ['id' => 123]);
}
}
Event Structure
Events are JSON-serialized payloads with a type (e.g., user.created) and data:
{
"type": "user.created",
"data": { "id": 123, "name": "John Doe" }
}
Consumer Integration
supervisord or systemd).# config/packages/test/ecentria_api_events.yaml
ecentria_api_events:
rabbitmq:
host: 'localhost'
vhost: '/test'
Event Dispatching
The bundle automatically converts RabbitMQ messages into Symfony DomainEvent objects. Subscribe to events in your services:
// app/config/services.yaml
services:
App\EventListener\MyEventSubscriber:
tags:
- { name: kernel.event_subscriber }
Batch Processing For high-throughput scenarios, implement a batch consumer by extending the base consumer:
use Ecentria\APIEventsBundle\Consumer\BaseConsumer;
class BatchConsumer extends BaseConsumer
{
protected function processMessage($message)
{
// Process in batches (e.g., every 100 messages)
}
}
RabbitMQ Connection Issues
ConnectionException.config/packages/ecentria_api_events.yaml and ensure RabbitMQ is running:
rabbitmqctl status
config/packages/monolog.yaml:
handlers:
rabbitmq:
type: stream
path: "%kernel.logs_dir%/rabbitmq.log"
level: debug
Event Deserialization Errors
JsonException when processing messages.use Ecentria\APIEventsBundle\Validator\EventValidator;
class MyValidator extends EventValidator
{
public function validate($data)
{
if (!isset($data['type'])) {
throw new \RuntimeException('Missing event type');
}
}
}
Register it in services.yaml:
services:
App\Validator\MyValidator:
tags: [ecentria_api_events.validator]
Duplicate Events
ack) may fail if the consumer crashes.// In your custom consumer
$this->channel->basic_qos(null, 1, null); // Prefetch 1 message
$this->channel->basic_consume($queue, '', false, true, false, false, [$this, 'handleDelivery']);
Performance Bottlenecks
basic_qos(null, 100)).use Symfony\Component\Messenger\MessageBusInterface;
class MyService
{
public function __construct(private MessageBusInterface $bus) {}
public function handleEvent(DomainEvent $event)
{
$this->bus->dispatch(new AsyncEvent($event->getData()));
}
}
Testing
public function testEventSubscriber()
{
$event = new DomainEvent(['type' => 'test.event', 'data' => []]);
$subscriber = new MyEventSubscriber();
$subscriber->onDomainEvent($event);
$this->assertTrue(true); // Add assertions
}
composer require enqueue/rabbitmq
Extending the Bundle
DomainEvent class:
namespace App\Event;
use Ecentria\APIEventsBundle\Event\DomainEvent;
class CustomEvent extends DomainEvent
{
public function getCustomData()
{
return $this->data['custom_field'] ?? null;
}
}
# config/packages/ecentria_api_events.yaml
ecentria_api_events:
routing:
'user.*.': 'user_events_queue'
'order.*.': 'order_events_queue'
Monitoring
use Prometheus\CollectorRegistry;
class ConsumerMonitor
{
public function __invoke($message)
{
$registry->getCounter('api_events_processed')->inc();
}
}
// In your consumer
try {
$this->processMessage($message);
} catch (\Throwable $e) {
error_log('Event processing failed: ' . $e->getMessage());
$this->channel->basic_nack($deliveryInfo['delivery_tag'], false, true);
}
Security
use Symfony\Component\Validator\Validator\ValidatorInterface;
class SecureEventSubscriber
{
public function __construct(private ValidatorInterface $validator) {}
public function onDomainEvent(DomainEvent $event)
{
$errors = $this->validator->validate($event->getData());
if (count($errors) > 0) {
throw new \RuntimeException('Invalid event data');
}
}
}
How can I help you explore Laravel packages today?