Skip to content

Commit 5c3c3c3

Browse files
committed
improve docs
1 parent 297eec1 commit 5c3c3c3

7 files changed

Lines changed: 238 additions & 65 deletions

File tree

README.md

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,76 +42,111 @@ See the [adapter list](docs/guide/en/adapter-list.md) and follow the adapter-spe
4242
> In this mode messages are processed immediately in the same process, so it won't provide true
4343
> async execution, but the code stays the same when you switch to a real adapter.
4444
45-
### 2. Configure the queue
45+
### 2. Prepare a message and handler
4646

47-
#### Configuration with [yiisoft/config](https://github.com/yiisoft/config)
48-
49-
**If you use [yiisoft/app](https://github.com/yiisoft/app) or [yiisoft/app-api](https://github.com/yiisoft/app-api)**
50-
51-
Add queue configuration to your application `$params` config. In [yiisoft/app](https://github.com/yiisoft/app)/[yiisoft/app-api](https://github.com/yiisoft/app-api) templates it's typically the `config/params.php` file.
52-
_If your project structure differs, put it into any params config file that is loaded by [yiisoft/config](https://github.com/yiisoft/config)._
53-
54-
Minimal configuration example:
47+
Define a message class for the work to be done — a simple value object with typed properties:
5548

5649
```php
57-
return [
58-
'yiisoft/queue' => [
59-
'handlers' => [
60-
'message-type' => [FooHandler::class, 'handle'],
61-
],
62-
],
63-
];
64-
```
50+
use Yiisoft\Queue\Message\Message;
6551

66-
[Advanced configuration with `yiisoft/config`](docs/guide/en/configuration-with-config.md)
52+
final class DownloadFileMessage extends Message
53+
{
54+
public const TYPE = 'download-file';
6755

68-
#### Manual configuration
56+
public function __construct(
57+
public readonly string $url,
58+
public readonly string $destinationPath,
59+
) {}
6960

70-
For setting up all classes manually, see the [Manual configuration](docs/guide/en/configuration-manual.md) guide.
61+
public static function fromData(string $type, mixed $data): static
62+
{
63+
if ($type !== self::TYPE) {
64+
throw new \InvalidArgumentException("Expected type \"" . self::TYPE . "\", got \"$type\".");
65+
}
66+
if (!is_array($data)
67+
|| !is_string($data['url'] ?? null)
68+
|| !is_string($data['destinationPath'] ?? null)
69+
) {
70+
throw new \InvalidArgumentException('Invalid data for ' . self::class . '.');
71+
}
72+
return new self($data['url'], $data['destinationPath']);
73+
}
7174

72-
### 3. Prepare a handler
75+
public function getType(): string
76+
{
77+
return self::TYPE;
78+
}
7379

74-
You need to create a handler class that will process the queue messages. The most simple way is to implement the `MessageHandlerInterface`. Let's create an example for remote file processing:
80+
public function getData(): array
81+
{
82+
return ['url' => $this->url, 'destinationPath' => $this->destinationPath];
83+
}
84+
}
85+
```
86+
87+
Then create a handler that processes it:
7588

7689
```php
7790
use Yiisoft\Queue\Message\MessageInterface;
7891
use Yiisoft\Queue\Message\MessageHandlerInterface;
7992

8093
final readonly class RemoteFileHandler implements MessageHandlerInterface
8194
{
82-
// These dependencies will be resolved on handler creation by the DI container
8395
public function __construct(
8496
private FileDownloader $downloader,
8597
private FileProcessor $processor,
8698
) {}
8799

88-
// Every received message will be processed by this method
89-
public function handle(MessageInterface $downloadMessage): void
100+
public function handle(MessageInterface $message): void
90101
{
91-
$url = $downloadMessage->getData()['url'];
92-
$localPath = $this->downloader->download($url);
102+
assert($message instanceof DownloadFileMessage);
103+
$localPath = $this->downloader->download($message->url, $message->destinationPath);
93104
$this->processor->process($localPath);
94105
}
95106
}
96107
```
97108

98-
### 4. Send (produce/push) a message to a queue
109+
### 3. Configure the queue
110+
111+
#### Configuration with [yiisoft/config](https://github.com/yiisoft/config)
112+
113+
**If you use [yiisoft/app](https://github.com/yiisoft/app) or [yiisoft/app-api](https://github.com/yiisoft/app-api)**
114+
115+
Add queue configuration to your application `$params` config. In [yiisoft/app](https://github.com/yiisoft/app)/[yiisoft/app-api](https://github.com/yiisoft/app-api) templates it's typically the `config/params.php` file.
116+
_If your project structure differs, put it into any params config file that is loaded by [yiisoft/config](https://github.com/yiisoft/config)._
99117

100-
To send a message to the queue, you need to get the queue instance and call the `push()` method. Typically, with Yii Framework you'll get a `Queue` instance as a dependency of a service.
118+
Minimal configuration example:
101119

102120
```php
121+
return [
122+
'yiisoft/queue' => [
123+
'handlers' => [
124+
DownloadFileMessage::TYPE => RemoteFileHandler::class,
125+
],
126+
],
127+
];
128+
```
129+
130+
[Advanced configuration with `yiisoft/config`](docs/guide/en/configuration-with-config.md)
131+
132+
#### Manual configuration
103133

104-
final readonly class Foo {
134+
For setting up all classes manually, see the [Manual configuration](docs/guide/en/configuration-manual.md) guide.
135+
136+
### 4. Send (produce/push) a message to a queue
137+
138+
To send a message to the queue, get the queue instance and call `push()`. Typically the queue is injected as a dependency:
139+
140+
```php
141+
final readonly class Foo
142+
{
105143
public function __construct(private QueueInterface $queue) {}
106144

107145
public function bar(): void
108146
{
109-
$this->queue->push(new Message(
110-
// The first parameter is the message type used to resolve the handler which will process the message
111-
RemoteFileHandler::class,
112-
// The second parameter is the data that will be passed to the handler.
113-
// It should be serializable to JSON format
114-
['url' => 'https://example.com/file-path.csv'],
147+
$this->queue->push(new DownloadFileMessage(
148+
url: 'https://example.com/file-path.csv',
149+
destinationPath: '/tmp/file-path.csv',
115150
));
116151
}
117152
}

docs/guide/en/configuration-manual.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ $logger = new NullLogger(); // replace with your PSR-3 logger in production
3535

3636
// Define message handlers
3737
$handlers = [
38-
'file-download' => [FileDownloader::class, 'handle'],
39-
FileDownloader::class => [FileDownloader::class, 'handle'],
38+
DownloadFileMessage::TYPE => [FileDownloader::class, 'handle'],
4039
];
4140

4241
$callableFactory = new CallableFactory($container);
@@ -79,7 +78,7 @@ $queue = new Queue(
7978
);
8079

8180
// Now you can push messages
82-
$message = new \Yiisoft\Queue\Message\GenericMessage('file-download', ['url' => 'https://example.com/file.pdf']);
81+
$message = new DownloadFileMessage(url: 'https://example.com/file.pdf', destinationPath: '/tmp/file.pdf');
8382
$queue->push($message);
8483
```
8584

docs/guide/en/message-handler-advanced.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,51 @@ Handler definitions are configured in:
1414

1515
### Handlers mapped by short message type
1616

17-
Use a short stable message type when pushing a `Message` instead of a PHP class name:
17+
Use a short stable message type instead of a PHP class name. Define a dedicated message class where `getType()` returns that type:
1818

1919
```php
20-
use Yiisoft\Queue\Message\GenericMessage;
20+
use Yiisoft\Queue\Message\Message;
21+
22+
final class SendEmailMessage extends Message
23+
{
24+
public const TYPE = 'send-email';
25+
26+
public function __construct(
27+
public readonly string $to,
28+
public readonly string $subject,
29+
public readonly string $body,
30+
) {}
31+
32+
public static function fromData(string $type, mixed $data): static
33+
{
34+
if ($type !== self::TYPE) {
35+
throw new \InvalidArgumentException("Expected type \"" . self::TYPE . "\", got \"$type\".");
36+
}
37+
if (!is_array($data)
38+
|| !is_string($data['to'] ?? null)
39+
|| !is_string($data['subject'] ?? null)
40+
|| !is_string($data['body'] ?? null)
41+
) {
42+
throw new \InvalidArgumentException('Invalid data for ' . self::class . '.');
43+
}
44+
return new self($data['to'], $data['subject'], $data['body']);
45+
}
46+
47+
public function getType(): string
48+
{
49+
return self::TYPE;
50+
}
51+
52+
public function getData(): array
53+
{
54+
return ['to' => $this->to, 'subject' => $this->subject, 'body' => $this->body];
55+
}
56+
}
57+
```
2158

22-
new GenericMessage('send-email', ['data' => '...']); // "send-email" is the message type here
59+
```php
60+
new SendEmailMessage('user@example.com', 'Welcome', 'Thank you for registering.');
61+
// getType() returns "send-email" — used by the worker to look up the handler
2362
```
2463

2564
**Config**:

docs/guide/en/message-handler.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,34 @@ If your handler implements `Yiisoft\Queue\Message\MessageHandlerInterface`, you
1313
**Message**:
1414

1515
```php
16-
new \Yiisoft\Queue\Message\GenericMessage(\App\Queue\RemoteFileHandler::class, ['url' => '...']);
16+
use Yiisoft\Queue\Message\Message;
17+
18+
final class RemoteFileMessage extends Message
19+
{
20+
public function __construct(public readonly string $url) {}
21+
22+
public static function fromData(string $type, mixed $data): static
23+
{
24+
if (!is_array($data) || !is_string($data['url'] ?? null)) {
25+
throw new \InvalidArgumentException('Invalid data for ' . self::class . '.');
26+
}
27+
return new self($data['url']);
28+
}
29+
30+
public function getType(): string
31+
{
32+
return \App\Queue\RemoteFileHandler::class;
33+
}
34+
35+
public function getData(): array
36+
{
37+
return ['url' => $this->url];
38+
}
39+
}
40+
```
41+
42+
```php
43+
new RemoteFileMessage('https://...');
1744
```
1845

1946
**Handler**:

docs/guide/en/messages-and-handlers.md

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ This separation is intentional and important. Understanding it will save you fro
1212
A *producer* creates messages and pushes them onto the queue. A *consumer* (worker) pulls messages from the queue and invokes the matching handler.
1313

1414
```
15-
Producer side Consumer side
16-
───────────────────────────── ──────────────────────────────────
17-
new Message('send-email', …) →→→ Worker resolves handler → handles
18-
(payload only) (logic only)
15+
Producer side Consumer side
16+
───────────────────────────── ──────────────────────────────────
17+
new SendEmailMessage(…) →→→ Worker resolves handler → handles
18+
(payload only) (logic only)
1919
```
2020

2121
The producer only needs to know the message type and its data. It does not need to know anything about how the message will be processed, or even in which application.
@@ -30,21 +30,61 @@ This means the producer and consumer can be:
3030

3131
## Message: payload only
3232

33-
A message carries just enough data to perform the work:
33+
A message carries just enough data to perform the work. Defining a dedicated class for each message type makes your code
34+
self-documenting and type-safe:
3435

3536
```php
36-
new \Yiisoft\Queue\Message\GenericMessage('send-email', [
37-
'to' => 'user@example.com',
38-
'subject' => 'Welcome',
39-
]);
37+
use Yiisoft\Queue\Message\Message;
38+
39+
final class SendEmailMessage extends Message
40+
{
41+
public const TYPE = 'send-email';
42+
43+
public function __construct(
44+
public readonly string $to,
45+
public readonly string $subject,
46+
public readonly string $body,
47+
) {}
48+
49+
public static function fromData(string $type, mixed $data): static
50+
{
51+
if ($type !== self::TYPE) {
52+
throw new \InvalidArgumentException("Expected type \"" . self::TYPE . "\", got \"$type\".");
53+
}
54+
if (!is_array($data)
55+
|| !is_string($data['to'] ?? null)
56+
|| !is_string($data['subject'] ?? null)
57+
|| !is_string($data['body'] ?? null)
58+
) {
59+
throw new \InvalidArgumentException('Invalid data for ' . self::class . '.');
60+
}
61+
return new self($data['to'], $data['subject'], $data['body']);
62+
}
63+
64+
public function getType(): string
65+
{
66+
return self::TYPE;
67+
}
68+
69+
public function getData(): array
70+
{
71+
return ['to' => $this->to, 'subject' => $this->subject, 'body' => $this->body];
72+
}
73+
}
74+
```
75+
76+
Usage:
77+
78+
```php
79+
new SendEmailMessage('user@example.com', 'Welcome', 'Thank you for registering.');
4080
```
4181

4282
The message has:
4383

4484
- A **message type** — a string used by the worker to look up the correct handler.
45-
- A **data payload**arbitrary data the handler needs. Must be serializable.
85+
- A **data payload**typed properties serialized to JSON via `getData()`. Must be JSON-encodable.
4686

47-
The message has no methods, no business logic, no dependencies. It is a value object — a data wrapper.
87+
The message has no business logic, no dependencies. It is a value object — a typed data wrapper.
4888

4989
## Handler: logic only
5090

@@ -57,8 +97,8 @@ final class SendEmailHandler implements \Yiisoft\Queue\Message\MessageHandlerInt
5797

5898
public function handle(\Yiisoft\Queue\Message\MessageInterface $message): void
5999
{
60-
$data = $message->getData();
61-
$this->mailer->send($data['to'], $data['subject']);
100+
assert($message instanceof SendEmailMessage);
101+
$this->mailer->send($message->to, $message->subject, $message->body);
62102
}
63103
}
64104
```

docs/guide/en/queue-names.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ Pushing a message via DI:
6868

6969
```php
7070
use Yiisoft\Queue\QueueInterface;
71-
use Yiisoft\Queue\Message\GenericMessage;
7271

7372
final readonly class SendWelcomeEmail
7473
{
@@ -78,7 +77,7 @@ final readonly class SendWelcomeEmail
7877

7978
public function run(string $email): void
8079
{
81-
$this->queue->push(new GenericMessage('send-email', ['to' => $email]));
80+
$this->queue->push(new SendEmailMessage(to: $email, subject: 'Welcome!', body: 'Thank you for registering.'));
8281
}
8382
}
8483
```
@@ -105,7 +104,6 @@ If you have multiple queue names, inject `QueueProviderInterface` and call `get(
105104

106105
```php
107106
use Yiisoft\Queue\Provider\QueueProviderInterface;
108-
use Yiisoft\Queue\Message\GenericMessage;
109107

110108
final readonly class SendTransactionalEmail
111109
{
@@ -117,7 +115,7 @@ final readonly class SendTransactionalEmail
117115
{
118116
$this->queueProvider
119117
->get('emails')
120-
->push(new GenericMessage('send-email', ['to' => $email]));
118+
->push(new SendEmailMessage(to: $email, subject: 'Welcome!', body: 'Thank you for registering.'));
121119
}
122120
}
123121
```

0 commit comments

Comments
 (0)