Custom data processors

When there is no suitable data processor that prepares the variables needed for your content element or template, you can define a custom data processor by implementing \TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface .

You can find the example below in the TYPO3 Documentation Team extension EXT:examples.

Using a custom data processor in TypoScript

The data processor can be configured through a TypoScript setup configuration. A custom data processor can be used in the definition of a "new custom content element" as follows:

EXT:examples/Configuration/TypoScript/DataProcessors/Processors/CustomCategoryProcessor.typoscript
tt_content {
    examples_dataproccustom =< lib.contentElement
    examples_dataproccustom {
        templateName = DataProcCustom
        # Before TYPO3 v12.1 you have to give the fully-qualified class name of the processor
        # dataProcessing.10 = T3docs\Examples\DataProcessing\CustomCategoryProcessor
        # Since TYPO3 v12.1 one can also use a (in Services.yaml) configured alias
        dataProcessing.10 = custom-category
        dataProcessing.10 {
            as = categories
            categoryList.field = categories
        }
    }
}
Copied!

In the extension examples you can find the code in EXT:examples/Configuration/TypoScript/DataProcessors/Processors/CustomCategoryProcessor.typoscript.

In the field categories the comma-separated categories are stored.

Register an alias for the data processor (optional)

New in version 12.1

Instead of using the fully-qualified class name as data processor identifier (in the example above \T3docs\Examples\DataProcessing\CustomCategoryProcessor) you can also define a short alias in Configuration/Services.yaml:

EXT:examples/Configuration/Services.yaml
T3docs\Examples\DataProcessing\CustomCategoryProcessor:
    tags:
        - name: 'data.processor'
          identifier: 'custom-category'
Copied!

The alias custom-category can now be used as data processor identifier like in the TypoScript example above.

Implementing the custom data processor

The custom data processor must implement \TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface . The main method process() gets called with the following parameters:

ContentObjectRenderer $cObj
Receives the data of the current TypoScript context, in this case the data of the calling content element.
array $contentObjectConfiguration
Contains the configuration of the calling content element. In this example it is the configuration tt_content.examples_dataproccustom
array $processorConfiguration
Contains the configuration of the currently called data processor. In this example it is the value of as and the stdWrap configuration of the categoryList
array $processedData
On calling, contains the processed data of all previously called data processors on this same content element. Your custom data processor also stores the variables to be sent to the Fluid template here.

This is an example implementation of a custom data processor:

EXT:examples/Classes/DataProcessing/CustomCategoryProcessor.php
<?php

declare(strict_types=1);

/*
 * This file is part of the TYPO3 CMS project. [...]
 */

namespace T3docs\Examples\DataProcessing;

use T3docs\Examples\Domain\Repository\CategoryRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;

/**
 * Class for data processing comma separated categories
 */
class CustomCategoryProcessor implements DataProcessorInterface
{
    /**
     * Process data for the content element "My new content element"
     *
     * @param ContentObjectRenderer $cObj The data of the content element or page
     * @param array $contentObjectConfiguration The configuration of Content Object
     * @param array $processorConfiguration The configuration of this processor
     * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View)
     * @return array the processed data as key/value store
     */
    public function process(
        ContentObjectRenderer $cObj,
        array $contentObjectConfiguration,
        array $processorConfiguration,
        array $processedData
    ) {
        if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) {
            return $processedData;
        }
        // categories by comma separated list
        $categoryIdList = $cObj->stdWrapValue('categoryList', $processorConfiguration ?? []);
        $categories = [];
        if ($categoryIdList) {
            $categoryIdList = GeneralUtility::intExplode(',', (string)$categoryIdList, true);
            /** @var CategoryRepository $categoryRepository */
            $categoryRepository = GeneralUtility::makeInstance(CategoryRepository::class);
            foreach ($categoryIdList as $categoryId) {
                $categories[] = $categoryRepository->findByUid($categoryId);
            }
            // set the categories into a variable, default "categories"
            $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'categories');
            $processedData[$targetVariableName] = $categories;
        }
        return $processedData;
    }
}
Copied!

In the extension examples you can find the code in EXT:/examples/Classes/DataProcessing/CustomCategoryProcessor.php.

On being called, the CustomCategoryProcessor runs stdWrap on the calling ContentObjectRenderer, which has the data of the table tt_content in the calling content element.

The field categoryList gets configured in TypoScript as follows:

categoryList.field = categories
Copied!

stdWrap fetches the value of categoryList from tt_content.tx_examples_main_category of the currently calling content element.

Now the custom data processor processes the comma-separated values into an array of integers that represent uids of the table sys_category. It then fetches the category data from the CategoryRepository by calling findByUid.

The data of the category records then get stored in the desired key in the $processedData array.

To make the data processor more configurable, we test for a TypoScript if condition at the beginning, and name the key we use to store the data configurable by the configuration as.