Using the DataHandler in scripts
You can use the class \TYPO3\
in your
own scripts: Inject the Data
class, build a
$data
/$cmd
array you want to pass to the class, and call a few
methods.
Attention
Mind that these scripts have to be run in the
backend scope! There must be a global $GLOBALS
object.
Table of Contents
Using the DataHandler in a Symfony command
It is possible to use the DataHandler for scripts started from the command line or by the scheduler as well. You can do this by creating a Symfony Command.
These scripts use the _cli_
backend user. Before using the DataHandler in your
execute
method, you should make sure that this user is initialized like
this:
\TYPO3\CMS\Core\Core\Bootstrap::initializeBackendAuthentication();
If you forget to add the backend user authentication, an error similar to this will occur:
[1.2.1]: Attempt to modify table "pages" without permission
DataHandler examples
What follows are a few code listings with comments which will provide you with
enough knowledge to get started. It is assumed that you have populated
the $data
and $cmd
arrays correctly prior to these chunks of code.
The syntax for these two arrays is explained in the
DataHandler basics chapter.
Warning
A new Data
object should be created using
General
before each use.
It is a stateful service and has to be considered polluted after use. Do not
call Data
or Data
multiple time on the same instance.
The Data
class must not be injected into the constructor via
dependency injection. This can cause unexpected
side effects.
Submitting data
This is the most basic example of how to submit data into the database.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DataHandling;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyClass
{
public function submitData(): void
{
// Prepare the data array
$data = [
// ... the data ...
];
/** @var DataHandler $dataHandler */
// Do not inject or reuse the DataHander as it holds state!
// Do not use `new` as GeneralUtility::makeInstance handles dependencies
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
// Register the $data array inside DataHandler and initialize the
// class internally.
$dataHandler->start($data, []);
// Submit data and have all records created/updated.
$dataHandler->process_datamap();
}
}
Executing commands
The most basic way of executing commands:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DataHandling;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyClass
{
public function executeCommands(): void
{
/** @var DataHandler $dataHandler */
// Do not inject or reuse the DataHander as it holds state!
// Do not use `new` as GeneralUtility::makeInstance handles dependencies
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
// Prepare the cmd array
$cmd = [
// ... the cmd structure ...
];
// Registers the $cmd array inside the class and initialize the
// class internally.
$dataHandler->start([], $cmd);
// Execute the commands.
$dataHandler->process_cmdmap();
}
}
Clearing cache
In this example the cache clearing API is used. No data is submitted, no
commands are executed. Still you will have to initialize the class by
calling the start
method (which will initialize internal state).
Note
Clearing a given cache is possible only for users that are "admin" or have specific permissions to do so.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DataHandling;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyClass
{
public function clearCache(): void
{
/** @var DataHandler $dataHandler */
// Do not inject or reuse the DataHander as it holds state!
// Do not use `new` as GeneralUtility::makeInstance handles dependencies
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start([], []);
$dataHandler->clear_cacheCmd('all');
}
}
Caches are organized in groups. Clearing "all" caches will actually clear caches from the "all" group and not really all caches. Check the caching framework architecture section for more details about available caches and groups.
Complex data submission
Imagine the $data
array contains something like this:
$data = [
'pages' => [
'NEW_1' => [
'pid' => 456,
'title' => 'Title for page 1',
],
'NEW_2' => [
'pid' => 456,
'title' => 'Title for page 2',
],
],
];
This aims to create two new pages in the page with uid "456". In the following code this is submitted to the database. Notice the reversing of the order of the array: This is done because otherwise "page 1" is created first, then "page 2" in the same PID meaning that "page 2" will end up above "page 1" in the order. Reversing the array will create "page 2" first and then "page 1" so the "expected order" is preserved.
To insert a record after a given record, set the other record's negative
uid
as pid
in the new record you're setting as data.
Apart from this a "signal" will be send that the page tree should be updated at the earliest occasion possible. Finally, the cache for all pages is cleared.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DataHandling;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyClass
{
public function submitComplexData(): void
{
$data = [
'pages' => [
'NEW_1' => [
'pid' => 456,
'title' => 'Title for page 1',
],
'NEW_2' => [
'pid' => 456,
'title' => 'Title for page 2',
],
],
];
/** @var DataHandler $dataHandler */
// Do not inject or reuse the DataHander as it holds state!
// Do not use `new` as GeneralUtility::makeInstance handles dependencies
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->reverseOrder = true;
$dataHandler->start($data, []);
$dataHandler->process_datamap();
BackendUtility::setUpdateSignal('updatePageTree');
$dataHandler->clear_cacheCmd('pages');
}
}
Both data and commands executed with alternative user object
In this case it is shown how you can use the same object instance to submit both data and execute commands if you like. The order will depend on the order in the code.
First the start
method is called, but this time with the third
possible argument which is an alternative $GLOBALS
object.
This allows you to force another backend user account to create stuff in the
database. This may be useful in certain special cases. Normally you
should not set this argument since you want DataHandler to use the global
$GLOBALS
.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DataHandling;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyClass
{
public function useAlternativeUser(BackendUserAuthentication $alternativeBackendUser): void
{
// Prepare the data array
$data = [
// ... the data ...
];
// Prepare the cmd array
$cmd = [
// ... the cmd structure ...
];
/** @var DataHandler $dataHandler */
// Do not inject or reuse the DataHander as it holds state!
// Do not use `new` as GeneralUtility::makeInstance handles dependencies
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($data, $cmd, $alternativeBackendUser);
$dataHandler->process_datamap();
$dataHandler->process_cmdmap();
}
}
Error handling
The data handler has a property error
as an array
.
In this property, the data handler collects all errors.
You can use these, for example, for logging or other error handling.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DataHandling;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class MyClass
{
public function __construct(
private readonly LoggerInterface $logger,
) {}
public function handleError(): void
{
/** @var DataHandler $dataHandler */
// Do not inject or reuse the DataHander as it holds state!
// Do not use `new` as GeneralUtility::makeInstance handles dependencies
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
// ... previous call of DataHandler's process_datamap() or process_cmdmap()
if ($dataHandler->errorLog !== []) {
$this->logger->error('Error(s) while creating content element');
foreach ($dataHandler->errorLog as $log) {
// handle error, for example, in a log
$this->logger->error($log);
}
}
}
}