Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow usage of message handler classes as message handler names #218

Merged
merged 4 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ Each queue task consists of two parts:
`Yiisoft\Queue\Message\Message`. For more complex cases you should implement the interface by your own.
2. A message handler is a callable called by a `Yiisoft\Queue\Worker\Worker`. The handler handles each queue message.

For example, if you need to download and save a file, your message may look like the following:
For example, if you need to download and save a file, your message creation may look like the following:
- Message handler as the first parameter
- Message data as the second parameter

```php
$data = [
'url' => $url,
'destinationFile' => $filename,
];
$message = new \Yiisoft\Queue\Message\Message('file-download', $data);
$message = new \Yiisoft\Queue\Message\Message(FileDownloader::class, $data);
```

Then you should push it to the queue:
Expand Down Expand Up @@ -93,9 +95,8 @@ class FileDownloader
The last thing we should do is to create a configuration for the `Yiisoft\Queue\Worker\Worker`:

```php
$handlers = ['file-download' => [new FileDownloader('/path/to/save/files'), 'handle']];
$worker = new \Yiisoft\Queue\Worker\Worker(
$handlers, // Here it is
[],
$logger,
$injector,
$container
Expand Down Expand Up @@ -134,6 +135,28 @@ $status->isReserved();
$status->isDone();
```

## Custom handler names
### Custom handler names

By default, when you push a message to the queue, the message handler name is the fully qualified class name of the handler.
This can be useful for most cases, but sometimes you may want to use a shorter name or arbitrary string as the handler name.
This can be useful when you want to reduce the amount of data being passed or when you communicate with external systems.

To use a custom handler name before message push, you can pass it as the first argument `Message` when creating it:
```php
new Message('handler-name', $data);
```

To use a custom handler name on message consume, you should configure handler mapping for the `Worker` class:
```php
$worker = new \Yiisoft\Queue\Worker\Worker(
['handler-name' => FooHandler::class],
$logger,
$injector,
$container
);
```

## Different queue channels

Often we need to push to different queue channels with an only application. There is the `QueueFactory` class to make
Expand Down
10 changes: 10 additions & 0 deletions src/Message/MessageHandlerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Queue\Message;

interface MessageHandlerInterface
{
public function handle(MessageInterface $message): void;
}
13 changes: 13 additions & 0 deletions src/Worker/Worker.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Throwable;
use Yiisoft\Injector\Injector;
use Yiisoft\Queue\Exception\JobFailureException;
use Yiisoft\Queue\Message\MessageHandlerInterface;
use Yiisoft\Queue\Message\MessageInterface;
use Yiisoft\Queue\Middleware\Consume\ConsumeFinalHandler;
use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher;
Expand Down Expand Up @@ -77,6 +78,18 @@ public function process(MessageInterface $message, QueueInterface $queue): Messa
private function getHandler(string $name): ?callable
{
if (!array_key_exists($name, $this->handlersCached)) {
$definition = $this->handlers[$name] ?? null;
if ($definition === null && $this->container->has($name)) {
$handler = $this->container->get($name);
if ($handler instanceof MessageHandlerInterface) {
$this->handlersCached[$name] = $handler->handle(...);

return $this->handlersCached[$name];
}

return null;
}

$this->handlersCached[$name] = $this->prepare($this->handlers[$name] ?? null);
}

Expand Down
24 changes: 24 additions & 0 deletions tests/Integration/MessageConsumingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsumeInterface;
use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher;
use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailureInterface;
use Yiisoft\Queue\Tests\Integration\Support\TestHandler;
use Yiisoft\Queue\Tests\TestCase;
use Yiisoft\Queue\Worker\Worker;

Expand Down Expand Up @@ -41,4 +42,27 @@ public function testMessagesConsumed(): void

$this->assertEquals($messages, $this->messagesProcessed);
}

public function testMessagesConsumedByHandlerClass(): void
{
$handler = new TestHandler();
$container = $this->createMock(ContainerInterface::class);
$container->method('get')->with(TestHandler::class)->willReturn($handler);
$container->method('has')->with(TestHandler::class)->willReturn(true);
$worker = new Worker(
[],
new NullLogger(),
new Injector($container),
$container,
new ConsumeMiddlewareDispatcher($this->createMock(MiddlewareFactoryConsumeInterface::class)),
new FailureMiddlewareDispatcher($this->createMock(MiddlewareFactoryFailureInterface::class), [])
);

$messages = [1, 'foo', 'bar-baz'];
foreach ($messages as $message) {
$worker->process(new Message(TestHandler::class, $message), $this->getQueue());
}

$this->assertEquals($messages, $handler->messagesProcessed);
}
}
20 changes: 20 additions & 0 deletions tests/Integration/Support/TestHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Queue\Tests\Integration\Support;

use Yiisoft\Queue\Message\MessageHandlerInterface;
use Yiisoft\Queue\Message\MessageInterface;

final class TestHandler implements MessageHandlerInterface
{
public function __construct(public array $messagesProcessed = [])
{
}

public function handle(MessageInterface $message): void
{
$this->messagesProcessed[] = $message->getData();
}
}
Loading