Custom reaction type

A custom reaction type may be useful, if the create database record type is not sufficient.

As an example the following scenario is used:

We want to synchronize products from an external system into TYPO3. As the synchronization may be time-consuming (think of synchronizing images), the real work is done by a command. Therefore, the reaction receives an ID from a product which was added or changed. This ID is stored in the sys_registry table.

A command (which is not part of this example) runs regularly and synchronizes every product ID stored in the registry entry into TYPO3.

You can find the implemented reaction type in the EXT:examples extension.

Create the reaction type

To create a custom reaction type, we add a class which implements the EXT:reactions/Classes/Reaction/ReactionInterface.php (GitHub):

EXT:examples/Classes/Reaction/ExampleReactionType.php
<?php

declare(strict_types=1);

namespace T3docs\Examples\Reaction;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use TYPO3\CMS\Core\Registry;
use TYPO3\CMS\Reactions\Model\ReactionInstruction;
use TYPO3\CMS\Reactions\Reaction\ReactionInterface;

class ExampleReactionType implements ReactionInterface
{
    private const REGISTRY_KEY = 'changed_ids';

    public function __construct(
        private readonly Registry $registry,
        private readonly ResponseFactoryInterface $responseFactory,
        private readonly StreamFactoryInterface $streamFactory,
    ) {}

    public static function getType(): string
    {
        return 'example-reaction-type';
    }

    public static function getDescription(): string
    {
        return 'Example reaction type';
    }

    public static function getIconIdentifier(): string
    {
        return 'tx_examples-dummy';
    }

    public function react(
        ServerRequestInterface $request,
        array $payload,
        ReactionInstruction $reaction
    ): ResponseInterface {
        $id = $payload['id'] ?? 0;
        if ($id <= 0) {
            $data = [
                'success' => false,
                'error' => 'id not given',
            ];

            return $this->jsonResponse($data, 400);
        }

        $this->updateRegistryEntry($id);

        return $this->jsonResponse(['success' => true]);
    }

    private function updateRegistryEntry(int $id): void
    {
        $ids = $this->registry->get('tx_examples', self::REGISTRY_KEY) ?? [];
        $ids[] = $id;
        $ids = array_unique($ids);
        $this->registry->set('tx_examples', self::REGISTRY_KEY, $ids);
    }

    private function jsonResponse(array $data, int $statusCode = 201): ResponseInterface
    {
        return $this->responseFactory
            ->createResponse($statusCode)
            ->withHeader('Content-Type', 'application/json')
            ->withBody($this->streamFactory->createStream(json_encode($data, JSON_THROW_ON_ERROR)));
    }
}
Copied!

You can use constructor injection to inject necessary dependencies.

Add reaction type to select list in backend module

In a next step we add the newly created reaction type to the list of reaction types in the backend:

EXT:examples/Configuration/TCA/Overrides/sys_reaction.php
<?php

defined('TYPO3') or die();

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
    'sys_reaction',
    'reaction_type',
    [
        'label' => \T3docs\Examples\Reaction\ExampleReactionType::getDescription(),
        'value' => \T3docs\Examples\Reaction\ExampleReactionType::getType(),
        'icon' => \T3docs\Examples\Reaction\ExampleReactionType::getIconIdentifier(),
    ]
);
Copied!

Now, our newly created type is displayed and can be selected:

Selecting our custom reaction type

Selecting our custom reaction type