Version 1.0.0
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
vendor/
|
||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright 2025 Viktor S. <thinlineseverywhere@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the “Software”),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
448
README.md
Normal file
448
README.md
Normal 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,
|
||||
]
|
||||
```
|
||||
42
composer.json
Normal file
42
composer.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "diffhead/laravel-rabbitmq",
|
||||
"description": "A laravel package for events emitting between services using RabbitMQ as message broker.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"laravel", "rabbitmq", "event", "emit", "microservice",
|
||||
"pipeline", "data exchanging", "message broker"
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Diffhead\\PHP\\LaravelRabbitMQ\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Diffhead\\PHP\\LaravelRabbitMQ\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"laravel/framework": "^10 || ^11.0 || ^12.0",
|
||||
"php-amqplib/php-amqplib": "^3.7",
|
||||
"diffhead/php-dto": "^1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"post-install-cmd": [
|
||||
"php artisan vendor:publish --provider=\"Diffhead\\PHP\\LaravelRabbitMQ\\ServiceProvider\""
|
||||
]
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Viktor S.",
|
||||
"email": "thinlineseverywhere@gmail.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
6296
composer.lock
generated
Normal file
6296
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
config/rabbitmq.php
Normal file
43
config/rabbitmq.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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', '/'),
|
||||
]
|
||||
],
|
||||
'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' => (string) env('RABBITMQ_EVENT_ROUTING_KEY', ''),
|
||||
],
|
||||
'mapper' => \Diffhead\PHP\LaravelRabbitMQ\Service\EventMapper::class,
|
||||
'map' => [
|
||||
/**
|
||||
* Example:
|
||||
*
|
||||
* \App\Shared\Event\User\UserCreated::class => [
|
||||
* 'queues' => ['portal.calendar.users'],
|
||||
* 'routing_keys' => ['user.created'],
|
||||
* ],
|
||||
* \App\Shared\Event\Meeting\MeetingCreated::class => [
|
||||
* 'queues' => ['portal.calendar.meetings'],
|
||||
* 'routing_keys' => ['meeting.created'],
|
||||
* ],
|
||||
*/
|
||||
],
|
||||
|
||||
]
|
||||
];
|
||||
118
src/Command/Consume.php
Normal file
118
src/Command/Consume.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Command;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Dto\ConsumerParameters;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Connection;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Exchange;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\ExchangeDeclaration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Queue;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\QueueBindings;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\QueueDeclaration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Configuration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Connector;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Message;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Consume extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'rabbitmq:consume {--connection=} {--queue=} {--exchange=} {--exchange-type=} {--exchange-is-default} {--routing-key=} {--tag=}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Consume messages from RabbitMQ';
|
||||
|
||||
private ?Connection $connection = null;
|
||||
|
||||
public function handle(
|
||||
Configuration $configuration,
|
||||
Connector $connector,
|
||||
Message $handler,
|
||||
LoggerInterface $logger
|
||||
): void {
|
||||
$params = ConsumerParameters::fromArray($this->options());
|
||||
$config = $configuration->get($params->connection->value() ?? 'default');
|
||||
|
||||
$queue = $this->getQueue($params);
|
||||
|
||||
$this->connection = $connector->connect($config, $queue);
|
||||
|
||||
$tag = $params->tag->value() ?? 'rabbitmq-laravel-consumer';
|
||||
|
||||
$consumer = function (AMQPMessage $message) use ($handler, $queue, $logger): void {
|
||||
try {
|
||||
$this->info(sprintf('Received message: %s', $message->getBody()));
|
||||
|
||||
$mergedQueue = $this->getMergedQueue($queue, $message);
|
||||
|
||||
$handler->handle($mergedQueue, $message);
|
||||
$message->ack();
|
||||
|
||||
$this->info('Message processed successfully.');
|
||||
} catch (Exception $e) {
|
||||
$message->nack();
|
||||
|
||||
$this->error(
|
||||
sprintf('Processing error: %s', $e->getMessage())
|
||||
);
|
||||
|
||||
$logger->error($e->getMessage());
|
||||
}
|
||||
};
|
||||
|
||||
$this->connection->channel()->basic_consume(
|
||||
queue: $queue->name(),
|
||||
consumer_tag: $tag,
|
||||
callback: $consumer
|
||||
);
|
||||
|
||||
$this->connection->channel()->consume();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->connection?->channel()->close();
|
||||
$this->connection?->connection()->close();
|
||||
}
|
||||
|
||||
private function getQueue(ConsumerParameters $params): Queue
|
||||
{
|
||||
$routingKey = $params->routingKey->value() ?? '';
|
||||
|
||||
return new Queue(
|
||||
name: $params->queue->value() ?? 'default',
|
||||
exchange: new Exchange(
|
||||
name: $params->exchange->value() ?? 'amq.direct',
|
||||
type: $params->exchangeType->value() ?? 'direct',
|
||||
isDefault: $params->exchangeIsDefault->value(),
|
||||
declaration: new ExchangeDeclaration()
|
||||
),
|
||||
declaration: new QueueDeclaration(),
|
||||
bindings: new QueueBindings($routingKey),
|
||||
);
|
||||
}
|
||||
|
||||
private function getMergedQueue(Queue $queue, AMQPMessage $message): Queue
|
||||
{
|
||||
return new Queue(
|
||||
name: $queue->name(),
|
||||
exchange: $queue->exchange(),
|
||||
declaration: $queue->declaration(),
|
||||
bindings: new QueueBindings(
|
||||
routingKey: $message->getRoutingKey(),
|
||||
nowait: $queue->bindings()->nowait(),
|
||||
arguments: $queue->bindings()->arguments(),
|
||||
ticket: $queue->bindings()->ticket()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
28
src/Dto/ConsumerParameters.php
Normal file
28
src/Dto/ConsumerParameters.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Dto;
|
||||
|
||||
use Diffhead\PHP\Dto\Dto;
|
||||
use Diffhead\PHP\Dto\Property;
|
||||
|
||||
/**
|
||||
* @property \Diffhead\PHP\Dto\Property<string|null> $connection
|
||||
* @property \Diffhead\PHP\Dto\Property<string|null> $queue
|
||||
* @property \Diffhead\PHP\Dto\Property<string|null> $exchange
|
||||
* @property \Diffhead\PHP\Dto\Property<string|null> $routingKey
|
||||
* @property \Diffhead\PHP\Dto\Property<string|null> $exchangeType
|
||||
* @property \Diffhead\PHP\Dto\Property<bool> $exchangeIsDefault
|
||||
* @property \Diffhead\PHP\Dto\Property<string|null> $tag
|
||||
*/
|
||||
class ConsumerParameters extends Dto
|
||||
{
|
||||
protected Property $connection;
|
||||
protected Property $queue;
|
||||
protected Property $exchange;
|
||||
protected Property $routingKey;
|
||||
protected Property $exchangeType;
|
||||
protected Property $exchangeIsDefault;
|
||||
protected Property $tag;
|
||||
}
|
||||
17
src/Event/Broadcast.php
Normal file
17
src/Event/Broadcast.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Event;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
interface Broadcast extends JsonSerializable
|
||||
{
|
||||
public function getConnection(): string;
|
||||
public function getQueue(): string;
|
||||
public function getExchange(): string;
|
||||
public function getExchangeType(): string;
|
||||
public function getExchangeIsDefault(): bool;
|
||||
public function getRoutingKey(): string;
|
||||
}
|
||||
9
src/Exception/AssociatedEventNotFound.php
Normal file
9
src/Exception/AssociatedEventNotFound.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class AssociatedEventNotFound extends RuntimeException {}
|
||||
13
src/Interface/EventMapper.php
Normal file
13
src/Interface/EventMapper.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Interface;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Queue;
|
||||
use App\Shared\Event\Event;
|
||||
|
||||
interface EventMapper
|
||||
{
|
||||
public function map(Queue $queue, array $payload): Event;
|
||||
}
|
||||
12
src/Interface/Serializer.php
Normal file
12
src/Interface/Serializer.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Interface;
|
||||
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
|
||||
interface Serializer
|
||||
{
|
||||
public function serialize(object $data): AMQPMessage;
|
||||
}
|
||||
12
src/Interface/Unserializer.php
Normal file
12
src/Interface/Unserializer.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Interface;
|
||||
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
|
||||
interface Unserializer
|
||||
{
|
||||
public function unserialize(AMQPMessage $message): array;
|
||||
}
|
||||
62
src/Listener/PublishEvent.php
Normal file
62
src/Listener/PublishEvent.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Listener;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\Serializer;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Exchange;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\ExchangeDeclaration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Queue;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\QueueBindings;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\QueueDeclaration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Configuration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Connector;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class PublishEvent implements ShouldQueue
|
||||
{
|
||||
public function __construct(
|
||||
private Configuration $configuration,
|
||||
private Connector $connector,
|
||||
private Serializer $serializer,
|
||||
) {}
|
||||
|
||||
public function handle(Broadcast $event): void
|
||||
{
|
||||
$config = $this->configuration->get($event->getConnection());
|
||||
|
||||
$queue = new Queue(
|
||||
name: $event->getQueue(),
|
||||
exchange: new Exchange(
|
||||
name: $event->getExchange(),
|
||||
type: $event->getExchangeType(),
|
||||
isDefault: $event->getExchangeIsDefault(),
|
||||
declaration: new ExchangeDeclaration()
|
||||
),
|
||||
declaration: new QueueDeclaration(),
|
||||
bindings: new QueueBindings(
|
||||
routingKey: $event->getRoutingKey()
|
||||
),
|
||||
);
|
||||
|
||||
$connection = $this->connector->connect($config, $queue);
|
||||
|
||||
try {
|
||||
$message = $this->serializer->serialize($event);
|
||||
|
||||
$connection->channel()->basic_publish(
|
||||
msg: $message,
|
||||
exchange: $queue->exchange()->name(),
|
||||
routing_key: $queue->bindings()->routingKey(),
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
throw $e;
|
||||
} finally {
|
||||
$connection->channel()->close();
|
||||
$connection->connection()->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/Object/Configuration.php
Normal file
41
src/Object/Configuration.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
class Configuration
|
||||
{
|
||||
public function __construct(
|
||||
private string $host,
|
||||
private int $port,
|
||||
private string $user,
|
||||
private string $password,
|
||||
private string $vhost,
|
||||
) {}
|
||||
|
||||
public function host(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function port(): int
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
public function user(): string
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function password(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function vhost(): string
|
||||
{
|
||||
return $this->vhost;
|
||||
}
|
||||
}
|
||||
26
src/Object/Connection.php
Normal file
26
src/Object/Connection.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
use PhpAmqpLib\Channel\AMQPChannel;
|
||||
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
||||
|
||||
class Connection
|
||||
{
|
||||
public function __construct(
|
||||
private AMQPStreamConnection $connection,
|
||||
private AMQPChannel $channel
|
||||
) {}
|
||||
|
||||
public function connection(): AMQPStreamConnection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function channel(): AMQPChannel
|
||||
{
|
||||
return $this->channel;
|
||||
}
|
||||
}
|
||||
35
src/Object/Exchange.php
Normal file
35
src/Object/Exchange.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
class Exchange
|
||||
{
|
||||
public function __construct(
|
||||
private string $name = 'amq.direct',
|
||||
private string $type = 'direct',
|
||||
private bool $isDefault = true,
|
||||
private ExchangeDeclaration $declaration,
|
||||
) {}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function isDefault(): bool
|
||||
{
|
||||
return $this->isDefault;
|
||||
}
|
||||
|
||||
public function declaration(): ExchangeDeclaration
|
||||
{
|
||||
return $this->declaration;
|
||||
}
|
||||
}
|
||||
53
src/Object/ExchangeDeclaration.php
Normal file
53
src/Object/ExchangeDeclaration.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
class ExchangeDeclaration
|
||||
{
|
||||
public function __construct(
|
||||
private bool $passive = false,
|
||||
private bool $durable = true,
|
||||
private bool $autoDelete = false,
|
||||
private bool $internal = false,
|
||||
private bool $nowait = false,
|
||||
private array $arguments = [],
|
||||
private mixed $ticket = null,
|
||||
) {}
|
||||
|
||||
public function passive(): bool
|
||||
{
|
||||
return $this->passive;
|
||||
}
|
||||
|
||||
public function durable(): bool
|
||||
{
|
||||
return $this->durable;
|
||||
}
|
||||
|
||||
public function internal(): bool
|
||||
{
|
||||
return $this->internal;
|
||||
}
|
||||
|
||||
public function autoDelete(): bool
|
||||
{
|
||||
return $this->autoDelete;
|
||||
}
|
||||
|
||||
public function nowait(): bool
|
||||
{
|
||||
return $this->nowait;
|
||||
}
|
||||
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function ticket(): ?int
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
}
|
||||
35
src/Object/Queue.php
Normal file
35
src/Object/Queue.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
class Queue
|
||||
{
|
||||
public function __construct(
|
||||
private string $name = 'default',
|
||||
private Exchange $exchange,
|
||||
private QueueDeclaration $declaration,
|
||||
private QueueBindings $bindings,
|
||||
) {}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function exchange(): Exchange
|
||||
{
|
||||
return $this->exchange;
|
||||
}
|
||||
|
||||
public function declaration(): QueueDeclaration
|
||||
{
|
||||
return $this->declaration;
|
||||
}
|
||||
|
||||
public function bindings(): QueueBindings
|
||||
{
|
||||
return $this->bindings;
|
||||
}
|
||||
}
|
||||
35
src/Object/QueueBindings.php
Normal file
35
src/Object/QueueBindings.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
class QueueBindings
|
||||
{
|
||||
public function __construct(
|
||||
private string $routingKey = '',
|
||||
private bool $nowait = false,
|
||||
private array $arguments = [],
|
||||
private mixed $ticket = null,
|
||||
) {}
|
||||
|
||||
public function routingKey(): string
|
||||
{
|
||||
return $this->routingKey;
|
||||
}
|
||||
|
||||
public function nowait(): bool
|
||||
{
|
||||
return $this->nowait;
|
||||
}
|
||||
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function ticket(): mixed
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
}
|
||||
53
src/Object/QueueDeclaration.php
Normal file
53
src/Object/QueueDeclaration.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Object;
|
||||
|
||||
class QueueDeclaration
|
||||
{
|
||||
public function __construct(
|
||||
private bool $passive = false,
|
||||
private bool $durable = true,
|
||||
private bool $exclusive = false,
|
||||
private bool $autoDelete = true,
|
||||
private bool $nowait = false,
|
||||
private array $arguments = [],
|
||||
private mixed $ticket = null,
|
||||
) {}
|
||||
|
||||
public function passive(): bool
|
||||
{
|
||||
return $this->passive;
|
||||
}
|
||||
|
||||
public function durable(): bool
|
||||
{
|
||||
return $this->durable;
|
||||
}
|
||||
|
||||
public function exclusive(): bool
|
||||
{
|
||||
return $this->exclusive;
|
||||
}
|
||||
|
||||
public function autoDelete(): bool
|
||||
{
|
||||
return $this->autoDelete;
|
||||
}
|
||||
|
||||
public function nowait(): bool
|
||||
{
|
||||
return $this->nowait;
|
||||
}
|
||||
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function ticket(): ?int
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
}
|
||||
30
src/Service/Configuration.php
Normal file
30
src/Service/Configuration.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Configuration as ConfigurationObject;
|
||||
use RuntimeException;
|
||||
|
||||
class Configuration
|
||||
{
|
||||
public function get(string $connection = 'default'): ConfigurationObject
|
||||
{
|
||||
$config = config(sprintf('rabbitmq.connections.%s', $connection));
|
||||
|
||||
if (empty($config)) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Not found rabbitmq config for connection %s', $connection)
|
||||
);
|
||||
}
|
||||
|
||||
return new ConfigurationObject(
|
||||
$config['host'],
|
||||
(int) $config['port'],
|
||||
$config['user'],
|
||||
$config['password'],
|
||||
$config['vhost'],
|
||||
);
|
||||
}
|
||||
}
|
||||
62
src/Service/Connector.php
Normal file
62
src/Service/Connector.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Configuration;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Connection;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Queue;
|
||||
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
||||
|
||||
class Connector
|
||||
{
|
||||
public function connect(Configuration $config, Queue $queue): Connection
|
||||
{
|
||||
$connection = new AMQPStreamConnection(
|
||||
$config->host(),
|
||||
$config->port(),
|
||||
$config->user(),
|
||||
$config->password(),
|
||||
$config->vhost()
|
||||
);
|
||||
|
||||
$channel = $connection->channel();
|
||||
|
||||
$channel->queue_declare(
|
||||
$queue->name(),
|
||||
$queue->declaration()->passive(),
|
||||
$queue->declaration()->durable(),
|
||||
$queue->declaration()->exclusive(),
|
||||
$queue->declaration()->autoDelete(),
|
||||
$queue->declaration()->nowait(),
|
||||
$queue->declaration()->arguments(),
|
||||
$queue->declaration()->ticket()
|
||||
);
|
||||
|
||||
if (! $queue->exchange()->isDefault()) {
|
||||
$channel->exchange_declare(
|
||||
$queue->exchange()->name(),
|
||||
$queue->exchange()->type(),
|
||||
$queue->exchange()->declaration()->passive(),
|
||||
$queue->exchange()->declaration()->durable(),
|
||||
$queue->exchange()->declaration()->autoDelete(),
|
||||
$queue->exchange()->declaration()->internal(),
|
||||
$queue->exchange()->declaration()->nowait(),
|
||||
$queue->exchange()->declaration()->arguments(),
|
||||
$queue->exchange()->declaration()->ticket()
|
||||
);
|
||||
}
|
||||
|
||||
$channel->queue_bind(
|
||||
$queue->name(),
|
||||
$queue->exchange()->name(),
|
||||
$queue->bindings()->routingKey(),
|
||||
$queue->bindings()->nowait(),
|
||||
$queue->bindings()->arguments(),
|
||||
$queue->bindings()->ticket()
|
||||
);
|
||||
|
||||
return new Connection($connection, $channel);
|
||||
}
|
||||
}
|
||||
19
src/Service/EventEmitter.php
Normal file
19
src/Service/EventEmitter.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class EventEmitter
|
||||
{
|
||||
public function __construct(
|
||||
private Dispatcher $dispatcher
|
||||
) {}
|
||||
|
||||
public function emit(object $event): void
|
||||
{
|
||||
$this->dispatcher->dispatch($event);
|
||||
}
|
||||
}
|
||||
49
src/Service/EventMapper.php
Normal file
49
src/Service/EventMapper.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Exception\AssociatedEventNotFound;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\EventMapper as EventMapperInterface;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Queue;
|
||||
use App\Shared\Event\Event;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
|
||||
class EventMapper implements EventMapperInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Application $app
|
||||
) {}
|
||||
|
||||
public function map(Queue $queue, array $payload): Event
|
||||
{
|
||||
$map = config('rabbitmq.event.map', []);
|
||||
|
||||
$queueName = $queue->name();
|
||||
$routingKey = $queue->bindings()->routingKey();
|
||||
|
||||
foreach ($map as $eventClass => $config) {
|
||||
$match = null;
|
||||
|
||||
if (! empty($config['queues'] ?? [])) {
|
||||
$match = in_array($queueName, $config['queues'], true);
|
||||
}
|
||||
|
||||
if (! empty($config['routing_keys'] ?? [])) {
|
||||
$match = is_null($match) ? true : $match;
|
||||
$match = $match && in_array($routingKey, $config['routing_keys'], true);
|
||||
}
|
||||
|
||||
if (is_null($match)) {
|
||||
return $this->app->make($eventClass, $payload);
|
||||
}
|
||||
|
||||
if (is_bool($match) && $match) {
|
||||
return $this->app->make($eventClass, $payload);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssociatedEventNotFound(json_encode($payload));
|
||||
}
|
||||
}
|
||||
27
src/Service/Message.php
Normal file
27
src/Service/Message.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\EventMapper;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\Unserializer;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Object\Queue;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
|
||||
class Message
|
||||
{
|
||||
public function __construct(
|
||||
private Unserializer $unserializer,
|
||||
private EventMapper $mapper,
|
||||
private EventEmitter $emitter,
|
||||
) {}
|
||||
|
||||
public function handle(Queue $queue, AMQPMessage $message): void
|
||||
{
|
||||
$payload = $this->unserializer->unserialize($message);
|
||||
$event = $this->mapper->map($queue, $payload);
|
||||
|
||||
$this->emitter->emit($event);
|
||||
}
|
||||
}
|
||||
26
src/Service/Serializer.php
Normal file
26
src/Service/Serializer.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\Serializer as SerializerInterface;
|
||||
use InvalidArgumentException;
|
||||
use JsonSerializable;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
|
||||
class Serializer implements SerializerInterface
|
||||
{
|
||||
public function serialize(object $data): AMQPMessage
|
||||
{
|
||||
if ($data instanceof JsonSerializable) {
|
||||
return new AMQPMessage(
|
||||
json_encode($data->jsonSerialize())
|
||||
);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
'Data should be an instance of BroadcastEvent'
|
||||
);
|
||||
}
|
||||
}
|
||||
16
src/Service/Unserializer.php
Normal file
16
src/Service/Unserializer.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Service;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\Unserializer as UnserializerInterface;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
|
||||
class Unserializer implements UnserializerInterface
|
||||
{
|
||||
public function unserialize(AMQPMessage $message): array
|
||||
{
|
||||
return json_decode($message->getBody(), true);
|
||||
}
|
||||
}
|
||||
96
src/ServiceProvider.php
Normal file
96
src/ServiceProvider.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ;
|
||||
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Command\Consume;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Event\Broadcast;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\EventMapper as EventMapperInterface;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\Serializer as SerializerInterface;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Interface\Unserializer as UnserializerInterface;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Listener\PublishEvent;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\EventMapper;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Serializer;
|
||||
use Diffhead\PHP\LaravelRabbitMQ\Service\Unserializer;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
|
||||
|
||||
class ServiceProvider extends LaravelServiceProvider
|
||||
{
|
||||
/**
|
||||
* @var array<int,string>
|
||||
*/
|
||||
private array $commands = [
|
||||
Consume::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string,array<int,string>>
|
||||
*/
|
||||
private array $listeners = [
|
||||
Broadcast::class => [
|
||||
PublishEvent::class,
|
||||
]
|
||||
];
|
||||
|
||||
public function register(): void
|
||||
{
|
||||
$this->registerServices();
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerConfigsPublishment();
|
||||
|
||||
$this->registerCommands();
|
||||
$this->registerListeners();
|
||||
}
|
||||
|
||||
private function registerServices(): void
|
||||
{
|
||||
$this->app->bind(
|
||||
SerializerInterface::class,
|
||||
config('rabbitmq.message.serializer', Serializer::class)
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
UnserializerInterface::class,
|
||||
config('rabbitmq.message.unserializer', Unserializer::class)
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
EventMapperInterface::class,
|
||||
config('rabbitmq.event.mapper', EventMapper::class)
|
||||
);
|
||||
}
|
||||
|
||||
private function registerCommands(): void
|
||||
{
|
||||
$this->commands($this->commands);
|
||||
}
|
||||
|
||||
private function registerListeners(): void
|
||||
{
|
||||
foreach ($this->listeners as $event => $listeners) {
|
||||
foreach ($listeners as $listener) {
|
||||
Event::listen($event, $listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function registerConfigsPublishment(): void
|
||||
{
|
||||
$this->publishes(
|
||||
[
|
||||
$this->configPath('config/rabbitmq.php') => config_path('rabbitmq.php'),
|
||||
],
|
||||
'config'
|
||||
);
|
||||
}
|
||||
|
||||
private function configPath(string $path): string
|
||||
{
|
||||
return sprintf('%s/../%s', __DIR__, $path);
|
||||
}
|
||||
}
|
||||
38
src/Trait/BroadcastEvent.php
Normal file
38
src/Trait/BroadcastEvent.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Diffhead\PHP\LaravelRabbitMQ\Trait;
|
||||
|
||||
trait BroadcastEvent
|
||||
{
|
||||
public function getConnection(): string
|
||||
{
|
||||
return config('rabbitmq.event.defaults.connection');
|
||||
}
|
||||
|
||||
public function getQueue(): string
|
||||
{
|
||||
return config('rabbitmq.event.defaults.queue');
|
||||
}
|
||||
|
||||
public function getExchange(): string
|
||||
{
|
||||
return config('rabbitmq.event.defaults.exchange');
|
||||
}
|
||||
|
||||
public function getExchangeType(): string
|
||||
{
|
||||
return config('rabbitmq.event.defaults.exchange_type');
|
||||
}
|
||||
|
||||
public function getExchangeIsDefault(): bool
|
||||
{
|
||||
return config('rabbitmq.event.defaults.exchange_is_default');
|
||||
}
|
||||
|
||||
public function getRoutingKey(): string
|
||||
{
|
||||
return config('rabbitmq.event.defaults.routing_key');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user