Version 1.0.0

This commit is contained in:
2025-12-12 11:41:44 +04:00
commit df7e485650
30 changed files with 7761 additions and 0 deletions

448
README.md Normal file
View File

@@ -0,0 +1,448 @@
# Laravel RabbitMQ Events
A package for working with RabbitMQ messages as Laravel events. Automatically serialize and publish events to RabbitMQ, and consume messages from queues by converting them back into events.
## Features
**Automatic event serialization** — Events implementing the `Broadcast` interface are automatically serialized and sent to RabbitMQ
**Message consumption** — Command to consume messages from RabbitMQ with automatic deserialization and event dispatching
**Default bindings** — The `BroadcastEvent` trait sets standard connection parameters for an event
**Flexible configuration** — Support for multiple connections and parameterization for each event
**Microservices architecture** — Ideal for data exchange between services
## Requirements
- PHP 8.1+
- Laravel 10, 11 or 12
- RabbitMQ server
## Installation
Install via Composer:
```bash
composer require diffhead/laravel-rabbitmq
```
The package will be automatically registered thanks to Laravel Service Provider Discovery.
## Configuration
Publish the configuration file:
```bash
php artisan vendor:publish --provider="Diffhead\PHP\LaravelRabbitMQ\ServiceProvider"
```
ThisEnvironment Variables
```env
# RabbitMQ Connection
RABBITMQ_HOST=localhost
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_VHOST=/
# Default event parameters
RABBITMQ_EVENT_CONNECTION=default
RABBITMQ_EVENT_QUEUE=default
RABBITMQ_EVENT_EXCHANGE=amq.direct
RABBITMQ_EVENT_EXCHANGE_TYPE=direct
RABBITMQ_EVENT_EXCHANGE_IS_DEFAULT=true
RABBITMQ_EVENT_ROUTING_KEY=
```
### Configuration Example
```php
return [
'connections' => [
'default' => [
'host' => env('RABBITMQ_HOST', 'localhost'),
'port' => env('RABBITMQ_PORT', 5672),
'user' => env('RABBITMQ_USER', 'guest'),
'password' => env('RABBITMQ_PASSWORD', 'guest'),
'vhost' => env('RABBITMQ_VHOST', '/'),
],
'secondary' => [
'host' => env('RABBITMQ_SECONDARY_HOST', 'localhost'),
'port' => env('RABBITMQ_SECONDARY_PORT', 5672),
'user' => env('RABBITMQ_SECONDARY_USER', 'guest'),
'password' => env('RABBITMQ_SECONDARY_PASSWORD', 'guest'),
'vhost' => env('RABBITMQ_SECONDARY_VHOST', '/'),
]
],
'message' => [
'serializer' => \Diffhead\PHP\LaravelRabbitMQ\Service\Serializer::class,
'unserializer' => \Diffhead\PHP\LaravelRabbitMQ\Service\Unserializer::class,
],
'event' => [
'defaults' => [
'connection' => env('RABBITMQ_EVENT_CONNECTION', 'default'),
'queue' => env('RABBITMQ_EVENT_QUEUE', 'default'),
'exchange' => env('RABBITMQ_EVENT_EXCHANGE', 'amq.direct'),
'exchange_type' => env('RABBITMQ_EVENT_EXCHANGE_TYPE', 'direct'),
'exchange_is_default' => (bool) env('RABBITMQ_EVENT_EXCHANGE_IS_DEFAULT', true),
'routing_key' => env('RABBITMQ_EVENT_ROUTING_KEY', ''),
],
'mapper' => \Diffhead\PHP\LaravelRabbitMQ\Service\EventMapper::class,
'map' => [
/**
* Map events to queues and routing keys
*/
\App\Events\User\UserCreated::class => [
'queues' => ['portal.users'],
'routing_keys' => ['user.created'],
],
\App\Events\Meeting\MeetingCreated::class => [
'queues' => ['portal.meetings'],
'routing_keys' => ['meeting.created'],
],
],
]
];
```
## Usage
### 1. Creating an Event for Publishing to RabbitMQ
Create an event that implements the `Broadcast` interface:
```php
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
use Illuminate\Foundation\Events\Dispatchable;
class UserCreated implements Broadcast
{
use Dispatchable, BroadcastEvent;
public function __construct(
public int $userId,
public string $email,
public string $name,
) {}
public function jsonSerialize(): array
{
return [
'userId' => $this->userId,
'email' => $this->email,
'name' => $this->name,
];
}
}
```
#### Event Parameters
When implementing the `Broadcast` interface, you must define the following methods:
- `getConnection(): string` — RabbitMQ connection name
- `getQueue(): string` — Queue name
- `getExchange(): string` — Exchange name
- `getExchangeType(): string` — Exchange type (direct, topic, fanout, headers)
- `getExchangeIsDefault(): bool` — Whether to use the default exchange
- `getRoutingKey(): string` — Routing key for the message
#### Using the BroadcastEvent Trait
The `BroadcastEvent` trait provides implementations of all methods using default parameters from configuration:
```php
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
class UserCreated implements Broadcast
{
use BroadcastEvent;
public function __construct(
public int $userId,
public string $email,
) {}
public function jsonSerialize(): array
{
return [
'userId' => $this->userId,
'email' => $this->email,
];
}
}
```
If you need special parameters for a specific event, override the necessary methods:
```php
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
class CriticalAlert implements Broadcast
{
use BroadcastEvent;
public function __construct(
public string $message,
) {}
public function getRoutingKey(): string
{
return 'alert.critical';
}
public function getExchange(): string
{
return 'alerts.topic';
}
public function getExchangeType(): string
{
return 'topic';
}
public function jsonSerialize(): array
{
return [
'message' => $this->message,
];
}
}
```
### 2. Publishing Events
Events are automatically published when dispatched:
```php
use App\Events\UserCreated;
/**
* Events implementing Broadcast are automatically sent to RabbitMQ
*/
UserCreated::dispatch(userId: 1, email: 'user@example.com', name: 'John Doe');
```
### 3. Consuming Messages from RabbitMQ
Use the `rabbitmq:consume` command to listen for messages:
```bash
#####################################################################################
#
# Has following options:
#
# --connection=default - Connection name from config
# --queue=default - Queue
# --exchange=amq.direct - Exchange name
# --exchange-type=direct - Exchange type
# --exchange-is-default - Exchange is default, required for default exchanges
# --routing-key=user.* - Listen routing keys, required for topic exchanges
# --tag=myconsumer - Consumer tag for rabbitmq
#
#####################################################################################
php artisan rabbitmq:consume
```
#### Full Consumer Startup Example
```bash
php artisan rabbitmq:consume \
--connection=default \
--queue=service.users \
--exchange=amq.direct \
--exchange-type=direct \
--routing-key=user.* \
--tag=service-users-consumer
```
### 4. Handling Received Events
When a message is received from RabbitMQ, it is automatically deserialized and dispatched as a Laravel event. You can listen to these events normally:
```php
namespace App\Listeners;
use App\Events\UserCreated;
use Illuminate\Support\Log;
class SendWelcomeEmail
{
public function handle(UserCreated $event): void
{
Log::info("User created: {$event->email}");
}
}
```
Register the listener in `app/Providers/EventServiceProvider.php`:
```php
protected $listen = [
\App\Events\UserCreated::class => [
\App\Listeners\SendWelcomeEmail::class,
],
];
```
## Architecture
### Event Publishing Flow
```
Laravel Event (Broadcast)
PublishEvent (Listener)
Serializer (JSON)
RabbitMQ Exchange
Queue
```
### Message Consumption Flow
```
RabbitMQ Queue
Message Consumer
Unserializer (JSON)
EventMapper (Event)
EventEmitter (Service)
Event Listeners
```
## Microservices Architecture Example
### Service 1: Publishes event
```php
namespace App\Events;
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
use Diffhead\PHP\LaravelRabbitMQ\Trait\BroadcastEvent;
class UserCreated implements Broadcast
{
use BroadcastEvent;
public function __construct(
public int $id,
public string $email,
public string $name,
) {}
public function getRoutingKey(): string
{
return 'user.created';
}
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'email' => $this->email,
'name' => $this->name,
];
}
}
```
```php
use App\Events\UserCreated;
/**
* Controller method
*/
public function store(Request $request)
{
$user = User::create($request->validated());
UserCreated::dispatch($user->id, $user->email, $user->name);
return response()->json($user, 201);
}
```
### Service 2: Receives event
Map event using configuration:
```php
'map' => [
\App\Events\UserCreated::class => [
'queues' => ['service2.users'],
'routing_keys' => ['user.created']
]
]
```
Then implement and register event listener:
```php
namespace App\Listeners;
use App\Events\UserCreated;
class SyncUserToCalendar
{
public function handle(UserCreated $event): void
{
CalendarUser::create([
'external_id' => $event->id,
'email' => $event->email,
'name' => $event->name,
]);
}
}
```
Start consumer
```bash
php artisan rabbitmq:consume --queue=service2.users --routing-key=user.* --exchange=amq.topic --exchange-type=topic --exchange-is-default
```
## Serialization
The package uses JSON for serialization/deserialization of data via `Serializer` and `Unserializer` interfaces.
### Custom Serialization
You can use your own serialization classes by implementing
interfaces and overriding following configuration entities:
```php
'message' => [
'serializer' => \App\Services\CustomSerializer::class,
'unserializer' => \App\Services\CustomUnserializer::class,
],
```
## Mapping
The package maps rabbitmq message to application events
### Custom mapping
You can use your own mapping logic by implementing EventMapper
interface and overriding the following configuration entity:
```php
'event' => [
'mapper' => \App\Services\CustomEventMapper::class,
]
```