---
title: "Using the DataHandler in scripts"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:using-tcemain@main"
source: "ApiOverview/DataHandler/UsingDataHandler/Index.rst"
rendered: "2026-09-28T12:36:38+00:00"
---

# Using the DataHandler in scripts {#using-tcemain}

You can use the class `\TYPO3\CMS\Core\DataHandling\DataHandler` in your
own scripts: Inject the `DataHandler` class, build a
`$data`/`$cmd` array you want to pass to the class, and call a few
methods.

> [!WARNING]
> **Attention**
>
> Mind that these scripts have to be run in the
> **backend scope**! There must be a global `$GLOBALS['BE_USER']` object.

**Table of Contents**

-   [Using the DataHandler in a Symfony command](https://docs.typo3.org/permalink/t3coreapi:using-the-datahandler-in-a-symfony-command@main)
-   [DataHandler examples](https://docs.typo3.org/permalink/t3coreapi:datahandler-examples@main)
-   [Error handling](https://docs.typo3.org/permalink/t3coreapi:error-handling@main)

## Using the DataHandler in a Symfony command {#datahandler-cli-command}

It is possible to use the DataHandler for scripts started from the command line
or by the [scheduler](https://docs.typo3.org/c/typo3/cms-scheduler/main/en-us/Index.html) as well. You can do this by
creating a [Symfony Command](https://docs.typo3.org/permalink/t3coreapi:cli-mode-command-controllers@main).

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:

**EXT:my_extension/Classes/Command/MyCommand.php**

```php
use TYPO3\CMS\Core\Core\Bootstrap;

Bootstrap::initializeBackendAuthentication();
```

If you forget to add the backend user authentication, an error similar to this
will occur:

```text
[1.2.1]: Attempt to modify table "pages" without permission
```

## DataHandler examples {#tcemain-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](https://docs.typo3.org/permalink/t3coreapi:datahandler-basics@main) chapter.

> [!WARNING]
> A new `DataHandler` object **should** be created using
> `GeneralUtility::makeInstance(DataHandler::class)` before each use.
> It is a stateful service and has to be considered polluted after use. Do not
> call `DataHandler::start()` or `DataHandler::process_datamap()`
> multiple time on the same instance.
>
> The `DataHandler` class **must not** be injected into the constructor via
> [dependency injection](https://docs.typo3.org/permalink/t3coreapi:dependencyinjection@main). This can cause unexpected
> side effects.

### Submitting data {#tcemain-submit-data}

This is the most basic example of how to submit data into the database.

**EXT:my_extension/Classes/DataHandling/MyClass.php**

```php
<?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 {#tcemain-execute-commands}

The most basic way of executing commands:

**EXT:my_extension/Classes/DataHandling/MyClass.php**

```php
<?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 {#tcemain-clear-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](https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/UserTsconfig/Options.html#useroptions) to do so.

**EXT:my_extension/Classes/MyClass.php**

```php
<?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](https://docs.typo3.org/permalink/t3coreapi:caching-architecture-core@main)
for more details about available caches and groups.

### Complex data submission {#tcemain-complex-submission}

Imagine the `$data` array contains something like this:

**EXT:my_extension/Classes/DataHandling/MyClass.php (excerpt)**

```php
$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.

**EXT:my_extension/Classes/DataHandling/MyClass.php**

```php
<?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 {#tcemain-data-command-user}

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['BE_USER']` 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['BE_USER']`.

**EXT:my_extension/Classes/DataHandling/MyClass.php**

```php
<?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 {#tcemain-error-handling}

The data handler has a property `errorLog` as an `array`.
In this property, the data handler collects all errors.
You can use these, for example, for logging or other error handling.

**EXT:my_extension/Classes/DataHandling/MyClass.php**

```php
<?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);
      }
    }
  }
}

```
