Skip to content
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
8 changes: 4 additions & 4 deletions docs/book/v1/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ It allows the main platform to return a response and remain responsive for new r
[![GitHub stars](https://img.shields.io/github/stars/dotkernel/queue)](https://github.com/dotkernel/queue/stargazers)
[![GitHub license](https://img.shields.io/github/license/dotkernel/queue)](https://github.com/dotkernel/queue/blob/1.0/LICENSE.md)

[![Build Status](https://github.com/mezzio/mezzio-skeleton/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/mezzio/mezzio-skeleton/actions/workflows/continuous-integration.yml)
[![codecov](https://codecov.io/gh/dotkernel/queue/graph/badge.svg?token=pexSf4wIhc)](https://codecov.io/gh/dotkernel/queue)
[![Qodana](https://github.com/dotkernel/queue/actions/workflows/qodana_code_quality.yml/badge.svg?branch=main)](https://github.com/dotkernel/queue/actions/workflows/qodana_code_quality.yml)
[![PHPStan](https://github.com/dotkernel/queue/actions/workflows/static-analysis.yml/badge.svg?branch=main)](https://github.com/dotkernel/queue/actions/workflows/static-analysis.yml)
[![Build Status](https://github.com/dotkernel/queue/actions/workflows/continuous-integration.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/queue/actions/workflows/continuous-integration.yml)
[![codecov](https://codecov.io/gh/dotkernel/queue/branch/1.0/graph/badge.svg?token=pexSf4wIhc)](https://codecov.io/gh/dotkernel/queue)
[![Qodana](https://github.com/dotkernel/queue/actions/workflows/qodana_code_quality.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/queue/actions/workflows/qodana_code_quality.yml)
[![PHPStan](https://github.com/dotkernel/queue/actions/workflows/static-analysis.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/queue/actions/workflows/static-analysis.yml)
51 changes: 51 additions & 0 deletions docs/book/v2/control-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Available commands and usage

The commands available are:

1. `GetFailedMessagesCommand.php (failed)` - returns logs with messages that failed to process (levelName:error)
2. `GetProcessedMessagesCommand.php (processed)` - returns logs with messages that were successfully processed (levelName:info)
3. `GetQueuedMessagesCommand (inventory)` - returns all queued messages from Redis stream 'messages'

The commands can be run in two different ways:

## CLI

To run the commands via CLI, use the following syntax:

```shell
php bin/cli.php failed --start="yyyy-mm-dd" --end="yyyy-mm-dd" --limit=int
```

```shell
php bin/cli.php processed --start="yyyy-mm-dd" --end="yyyy-mm-dd" --limit=int
```

```shell
php bin/cli.php inventory
```

## TCP message

To use commands using TCP messages, the following messages can be used:

```shell
echo "failed --start=yyyy-mm-dd --end=yyyy-mm-dd --limit=days" | socat -t1 - TCP:host:port
```

```shell
echo "processed --start=yyyy-mm-dd --end=yyyy-mm-dd --limit=days" | socat -t1 - TCP:host:port
```

In both cases, the flags are optional. Keep in mind if both `start` and `end` are set, `limit` will not be applied, it's only used when one of `start` or `end` is missing.

To be able to test the `processed` command, by default when processing the "control" message, it is logged as successfully processed with `"levelName":"info"` simulating that the message was processed successfully. To use it, run the following message:

```shell
echo "control" | socat -t1 - TCP:host:port
```

> Using `-t1` flag is not necessary but can be useful, it is used to set a timeout of n seconds for both reading and writing; after n second of inactivity, socat will terminate the connection. If the timeout is not set and the server does not respond or keep the connection open, the socat process could freeze indefinitely.

```shell
echo "inventory" | socat -t1 - TCP:host:port
```
206 changes: 206 additions & 0 deletions docs/book/v2/how-to/communication-with-queue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# COMMUNICATE WITH QUEUE

Communication with the [`Dotkernel Queue`](https://github.com/dotkernel/queue) can be achieved in two different ways: procedural and object-oriented.

## Procedural approach

The main advantage of the procedural code is its simplicity, it directly connects to the queue server, sends the prepared data, and then closes the connection. In this example, the code opens a TCP connection to `localhost:8556`, checks if the connection is successful, sends data in JSON format, and closes the socket. This makes the logic easy to follow and quick to implement. The disadvantage of this approach is the difficulty of reusing and extending the code. As the complexity of the project increases by repeatedly using this approach, the code becomes more difficult to maintain and not as flexible compared to the object-oriented approach.

```php
// collect the PAYLOAD that will be sent to Dotkernel Queue
$data = $anyTypeOfData;

// open TCP connection to Dotkernel Queue server
// replace localhost with the IP of the Dotkernel Queue server
// replace 8556 with the port number
$client = stream_socket_client("tcp://localhost:8556", $errno, $errstr, 30);

if (! $client) {
// log errors
error_log("Connection error: $errstr ($errno)");
} else {
// send the PAYLOAD to Dotkernel Queue
fwrite($client, json_encode($data) . "\n");
// make sure your message ends with a new line
// this allows the stream to know when the message is complete
// without it, the server might keep waiting, and not process what was sent

fclose($client);
// close connection
}
```

## Object-oriented approach

Using object-oriented approach, queue is accessed through dedicated classes and objects. Instead of calling functions directly, you instantiate objects that encapsulate the behavior of the queue. This approach offers reusability, maintainability, and cleaner separation of responsibilities, which is especially beneficial in larger applications.

To implement this approach you need to follow a few simple steps:

## Step 1 NotificationSystem module

Create a new directory under the Core directory with the name NotificationSystem or any other name.

Inside it, create a new `ConfigProvider.php`, a new `NotificationService.php` and then copy the code below.

ConfigProvider code:

```php
<?php

declare(strict_types=1);

namespace Core\NotificationSystem;

use Core\NotificationSystem\Service\NotificationService;
use Dot\DependencyInjection\Factory\AttributedServiceFactory;

/**
* @phpstan-type ConfigType array{
* dependencies: DependenciesType,
* }
* @phpstan-type DependenciesType array{
* factories: array<class-string, class-string>,
* }
*/
class ConfigProvider
{
/**
* @return ConfigType
*/
public function __invoke(): array
{
return [
'dependencies' => $this->getDependencies(),
];
}

/**
* @return DependenciesType
*/
public function getDependencies(): array
{
return [
'factories' => [
NotificationService::class => AttributedServiceFactory::class,
],
];
}
}
```

Service code:

```php
<?php

declare(strict_types=1);

namespace Core\NotificationSystem\Service;

use Core\User\Entity\User;
use Dot\DependencyInjection\Attribute\Inject;
use Laminas\Json\Encoder;
use Socket\Raw\Factory;
use Socket\Raw\Socket;

class NotificationService
{
#[Inject(
'config.notification.server',
)]
public function __construct(
// $config from local.php (see step 3)
private readonly array $config
) {
}

// open TCP connection to Dotkernel Queue
public function createClient(): Socket
{
$socketRawFactory = new Factory();
return $socketRawFactory->createClient(
$this->config['protocol'] . '://' . $this->config['host'] . ':' . $this->config['port']
);
}

// send the PAYLOAD to Dotkernel Queue
public function send(string $message): void
{
$this->createClient()->write($message . $this->config['eof']);
}

// encode processed data
protected function encodeEmailMessage(array $data): string
{
return Encoder::encode($data);
}

// process and send data
// change $data type depending on what you need
public function sendNewAccountNotification(mixed $data): void
{
$data['yourKey'] = $data;
$this->send($this->encodeEmailMessage($data));
}

//create your own custom methods to process data
}
```

## Step 2 Install new dependencies

After you have finished creating the new files, navigate to composer.json and add the new dependencies under the require key:

```json
{
"require": {
"ext-sockets": "*",
"clue/socket-raw": "^1.6.0"
}
}
```

Install new dependencies using:

```shell
composer install
```

Under the `autoload` → `psr-4` key add the newly created module:

```php
"Core\\NotificationSystem\\": "src/Core/src/NotificationSystem/src"
```

Run `composer dump-autoload` to regenerate Composer’s autoload files.

## Step 3 Configuration

Navigate to `config/config.php` and add your new Core ConfigProvider `Core\NotificationSystem\ConfigProvider::class`.

After you have added the new config provider, navigate to `config/autoload/local.php` and add a new config key used for server connection.
> **_NOTE:_** if you only have the local.php.dist file, duplicate it, and delete .dist from the copy's name.

```php
'notification' => [
'server' => [
'protocol' => 'tcp',
'host' => 'IP of the Dotkernel Queue ',
'port' => 'port number',
'eof' => "\n",
],
],
```

## Step 4 Service usage

Navigate to your handler, inject the new service and use your custom method where needed.

```php
#[Inject(
NotificationService::class
)]
public function __construct(
protected NotificationService $notificationService
) {
```
110 changes: 110 additions & 0 deletions docs/book/v2/how-to/send-emails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# SEND EMAILS

Using a queuing service solves problems such as server overload. For example, if a server receives a large number of requests, it tries to process them synchronously, resulting in long response times or even server crashes.

A concrete example is sending emails. While a series of tasks are running on the server, a task such as sending an email is passed to a queue and run in the background so the server can move on to the next task, while the queue composes the email and sends it. Tasks are queued and processed gradually (FIFO), depending on available resources.

To implement such a service, the [`send-email`](https://github.com/dotkernel/queue/tree/send-email) branch can be taken as a model.

> **_NOTE:_** The default branch 1.0 holds only the base code of Queue and provides essential features such as:
>
> * Adding messages to the queue
> * Retrieving and processing messages (FIFO)

## Step 1 Importing Core

Importing the Core is essential for the queue to communicate with the main application logic (for this case [`dotkernel admin`](https://github.com/dotkernel/admin)). Whether you copy the Core folder directly or add it as a submodule, it’s important to keep the Core in sync between the main project and the queue so that all classes, services, and configurations are available for message processing.

## Step 2 Core dependency

After adding the Core, you need to make sure all external dependencies are included in composer.json. Without them, Core functionalities (cache, mail, authentication, etc.) won’t work in the queue.

```json
{
"dotkernel/dot-cache": "^4.3",
"dotkernel/dot-data-fixtures": "^1.4.0",
"dotkernel/dot-errorhandler": "^5.0.0",
"dotkernel/dot-mail": "^5.3.0",
"laminas/laminas-authentication": "^2.18",
"mezzio/mezzio-authentication-oauth2": "^3.0.1",
"mezzio/mezzio-twigrenderer": "^2.19.0",
"ramsey/uuid": "^4.5.0",
"ramsey/uuid-doctrine": "^2.1.0",
"roave/psr-container-doctrine": "^6.2.0"
}
```

Adding Core modules under `autoload` → `psr-4` enables automatic class loading, so you don’t have to manually require each class.

```json
{
"autoload": {
"psr-4": {
"Queue\\": "src/",
"Core\\Admin\\": "src/Core/src/Admin/src",
"Core\\App\\": "src/Core/src/App/src",
"Core\\Security\\": "src/Core/src/Security/src",
"Core\\Setting\\": "src/Core/src/Setting/src",
"Core\\User\\": "src/Core/src/User/src"
}
}
}
```

## Step 3 Install new dependencies

Install all new dependencies using:

```shell
composer install
```

Running `composer install` ensures that all packages required for Core and the queue are downloaded. This includes third-party libraries and all project-specific dependencies.

## Step 4 Configuration

Navigate to `config/config.php` and add `ConfigProvider::class` file from all packages you installed.

```php
Mezzio\Twig\ConfigProvider::class,
Dot\Cache\ConfigProvider::class,
Dot\DataFixtures\ConfigProvider::class,
Dot\Mail\ConfigProvider::class,

// Core
Core\Admin\ConfigProvider::class,
Core\App\ConfigProvider::class,
Core\Security\ConfigProvider::class,
Core\Setting\ConfigProvider::class,
Core\User\ConfigProvider::class,
```

## Step 5 Database connection

Navigate to `config/autoload/local.php` and fill in the database connection details.
> **_NOTE:_** if you only have the local.php.dist file, duplicate it, and delete .dist from the copy's name.

```php
$databases = [
'default' => [
'host' => '',
'dbname' => '',
'user' => '',
'password' => '',
'port' => 3306,
'driver' => 'pdo_mysql',
'charset' => 'utf8mb4',
'collate' => 'utf8mb4_general_ci',
],
];
```

## Step 6 Email configuration

Inside your `config/autoload` folder create a new file named `mail.global.php`, copy this [file](https://github.com/dotkernel/queue/blob/send-email/config/autoload/mail.global.php) content and fill in your configuration. The queue will use these settings to send emails in the background.

## Step 7 Data management and emails sending

Once everything is installed and configured we can move on to handle the data in the queue. In the message handler for example `MessageHandler`, each message from the queue is processed, the email is composed, and then sent. By injecting the required services and using templates, the handler can send emails without blocking the main application, respecting FIFO and asynchronous processing.

In this [file](https://github.com/dotkernel/queue/blob/send-email/src/App/Message/MessageHandler.php) you can follow a simple example of how to create and send an email using data received from the queue inside the handler.
Loading