Agent Skill for TYPO3 DataHandler patterns. Use when working with TYPO3 database operations, record manipulation (create, update, copy, move, delete, localize), workspaces, versioning, staging, publishing, TCEmain hooks, cache clearing, or backend record processing. Essential for TYPO3 extension development.

0 stars
0 forks
14 views

SKILL.md


name: typo3-datahandler description: Agent Skill for TYPO3 DataHandler patterns. Use when working with TYPO3 database operations, record manipulation (create, update, copy, move, delete, localize), workspaces, versioning, staging, publishing, TCEmain hooks, cache clearing, or backend record processing. Essential for TYPO3 extension development.

TYPO3 DataHandler Skill

Expert patterns for working with TYPO3's DataHandler (formerly TCEmain) - the central API for all database record operations in the TYPO3 backend.

What is DataHandler?

The DataHandler (\TYPO3\CMS\Core\DataHandling\DataHandler) is TYPO3's core class for:

  • Creating new records in any TCA-configured table
  • Updating existing records
  • Copying records (with optional child elements)
  • Moving records between pages
  • Deleting/Undeleting records (soft delete)
  • Localizing records for multilingual sites
  • Versioning and Workspaces (staging, publishing, approval workflows)
  • Cache clearing operations

All operations respect TCA configuration, user permissions, and workspace states.

Workspaces Overview

Workspaces allow staging changes before publishing:

  • Live workspace (id = 0): Published content visible to visitors
  • Custom workspaces (id > 0): Staging areas for draft changes
  • DataHandler automatically creates versions when editing in a workspace
  • Use version command for publish/swap/discard operations

Quick Reference

Basic Usage Pattern

<?php
declare(strict_types=1);

use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;

// NEVER inject DataHandler - always create new instance
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($data, $cmd);
$dataHandler->process_datamap();  // For data operations
$dataHandler->process_cmdmap();   // For command operations

// Check for errors
if ($dataHandler->errorLog !== []) {
    foreach ($dataHandler->errorLog as $error) {
        // Handle error
    }
}

Data Array Syntax (Create/Update)

// Create new record (use NEW + unique ID)
$data['pages']['NEW123'] = [
    'pid' => 1,           // Parent page ID
    'title' => 'My Page',
    'hidden' => 0,
];

// Update existing record
$data['tt_content'][42] = [
    'header' => 'Updated Header',
    'bodytext' => 'New content',
];

// Reference NEW record in another NEW record
$data['sys_category']['NEW_cat'] = ['title' => 'New Category', 'pid' => 1];
$data['tt_content']['NEW_content'] = [
    'pid' => 1,
    'categories' => 'NEW_cat',  // References the category above
];

Command Array Syntax (Copy/Move/Delete)

// Delete record
$cmd['tt_content'][42]['delete'] = 1;

// Undelete record
$cmd['tt_content'][42]['undelete'] = 1;

// Copy to page (positive = first in page)
$cmd['tt_content'][42]['copy'] = 10;

// Copy after record (negative = after that UID)
$cmd['tt_content'][42]['copy'] = -55;

// Move to page
$cmd['tt_content'][42]['move'] = 10;

// Localize to language
$cmd['tt_content'][42]['localize'] = 2;  // Language UID

// Copy to language (free mode, no parent reference)
$cmd['tt_content'][42]['copyToLanguage'] = 2;

Expertise Areas

Data Operations (process_datamap())

  • Record creation with NEW placeholders
  • Record updates with field values
  • Inline (IRRE) record handling
  • FlexForm data submission
  • File references (FAL)
  • MM relation handling

Command Operations (process_cmdmap())

  • copy - Duplicate records with children
  • move - Relocate records
  • delete - Soft delete (respects TCA delete field)
  • undelete - Restore deleted records
  • localize - Create language overlays (connected mode)
  • copyToLanguage - Copy to language (free mode)
  • version - Workspace operations

Getting Created Record UIDs

// After process_datamap(), get the real UID
$realUid = $dataHandler->substNEWwithIDs['NEW123'];

// For copied records
$dataHandler->copyMappingArray_merged['tt_content'][42]; // Returns new UID

Cache Operations

$dataHandler->start([], []);
$dataHandler->clear_cacheCmd('pages');    // Clear all page cache
$dataHandler->clear_cacheCmd('all');      // Clear all caches (admin only)
$dataHandler->clear_cacheCmd(123);        // Clear specific page cache

Reference Files

  • references/data-array.md - Data array patterns and examples
  • references/command-array.md - Command operations reference
  • references/workspaces.md - Workspaces, versioning, staging, publishing
  • references/hooks-events.md - Hook into DataHandler operations
  • references/common-patterns.md - Typical use cases and solutions

Critical Rules

⚠️ NEVER Inject DataHandler

// ❌ WRONG - DataHandler is stateful
public function __construct(private DataHandler $dataHandler) {}

// ✅ CORRECT - Always create new instance
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);

⚠️ Backend Context Required

DataHandler requires $GLOBALS['BE_USER']. In CLI commands:

\TYPO3\CMS\Core\Core\Bootstrap::initializeBackendAuthentication();

⚠️ TCA Configuration Required

Fields must be configured in TCA to be processed. If type is none or invalid, DataHandler ignores the field.

⚠️ sys_file Table Blocked

Direct modifications to sys_file are blocked since TYPO3 11.5.35/12.4.11/13.0.1 for security.

Common Patterns

Create Page with Content

$data = [
    'pages' => [
        'NEW_page' => [
            'pid' => 1,
            'title' => 'New Page',
            'doktype' => 1,
        ],
    ],
    'tt_content' => [
        'NEW_content' => [
            'pid' => 'NEW_page',  // Reference to new page
            'CType' => 'text',
            'header' => 'Welcome',
            'bodytext' => 'Content here',
        ],
    ],
];

$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($data, []);
$dataHandler->process_datamap();

$pageUid = $dataHandler->substNEWwithIDs['NEW_page'];
$contentUid = $dataHandler->substNEWwithIDs['NEW_content'];

Bulk Operations with Ordering

// Create multiple records in correct order
$data = [
    'pages' => [
        'NEW_1' => ['pid' => 1, 'title' => 'Page 1'],
        'NEW_2' => ['pid' => 1, 'title' => 'Page 2'],
    ],
];

$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->reverseOrder = true;  // Preserve array order
$dataHandler->start($data, []);
$dataHandler->process_datamap();

Copy Record with Modifications

$cmd = [
    'tt_content' => [
        42 => [
            'copy' => [
                'action' => 'paste',
                'target' => 10,  // Page UID
                'update' => [
                    'header' => 'Copied: ' . $originalHeader,
                ],
            ],
        ],
    ],
];

Workspace Operations

// Publish (swap) a workspace version
$cmd['pages'][42]['version'] = [
    'action' => 'swap',
    'swapWith' => 123,  // Workspace version UID
];

// Discard workspace changes
$cmd['pages'][42]['version'] = [
    'action' => 'clearWSID',
];

// Set approval stage
$cmd['pages'][42]['version'] = [
    'action' => 'setStage',
    'stageId' => 10,  // -1=rejected, 0=editing, 1=review, 10=publish
    'comment' => 'Ready for review',
];

// Bypass workspace restrictions (use carefully!)
$dataHandler->bypassWorkspaceRestrictions = true;

See references/workspaces.md for complete workspace patterns.

Hooking Into DataHandler

Legacy Hooks (still supported)

// ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][]
    = \MyVendor\MyExt\Hook\DataHandlerHook::class;

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'][]
    = \MyVendor\MyExt\Hook\DataHandlerHook::class;

Hook Methods

class DataHandlerHook
{
    // Before record is saved
    public function processDatamap_preProcessFieldArray(
        array &$fieldArray,
        string $table,
        string|int $id,
        DataHandler $dataHandler
    ): void {}

    // After record is saved
    public function processDatamap_postProcessFieldArray(
        string $status,  // 'new' or 'update'
        string $table,
        string|int $id,
        array $fieldArray,
        DataHandler $dataHandler
    ): void {}

    // After all records processed
    public function processDatamap_afterAllOperations(DataHandler $dataHandler): void {}

    // Before command executed
    public function processCmdmap_preProcess(
        string $command,
        string $table,
        int $id,
        mixed $value,
        DataHandler $dataHandler,
        bool|string $pasteUpdate
    ): void {}

    // After command executed
    public function processCmdmap_postProcess(
        string $command,
        string $table,
        int $id,
        mixed $value,
        DataHandler $dataHandler,
        bool|string $pasteUpdate,
        bool|string $pasteDatamap
    ): void {}
}

Error Handling

$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($data, $cmd);
$dataHandler->process_datamap();
$dataHandler->process_cmdmap();

if ($dataHandler->errorLog !== []) {
    foreach ($dataHandler->errorLog as $error) {
        $this->logger->error('DataHandler error', ['message' => $error]);
    }
}

Verification Checklist

Before using DataHandler:

  • TCA is properly configured for all tables/fields
  • Backend user context exists ($GLOBALS['BE_USER'])
  • User has permissions for the operation
  • Creating new instance (not injecting)
  • Checking errorLog after operations
  • Using substNEWwithIDs to get created record UIDs

Documentation: TYPO3 DataHandler Reference

README

TYPO3 DataHandler Skill

Note: We're testing AI agent skills here. Maybe useful, maybe not. :)

Agent Skill for TYPO3 DataHandler (formerly TCEmain) patterns - the central API for all database record operations in TYPO3.

🔌 Compatibility

This is an Agent Skill following the Agent Skills specification developed by Anthropic.

Supported Platforms:

  • Cursor (via .cursor/skills/ or .cursor/rules/)
  • Claude Code (via ~/.claude/skills/ or project .claude/skills/)
  • GitHub Copilot (via skills-compatible configuration)
  • ✅ Other skills-compatible AI agents

Features

  • Data Operations: Record creation, updates, inline (IRRE) handling, FlexForm data
  • Command Operations: Copy, move, delete, undelete, localize, version
  • Workspaces & Versioning: Staging, publishing, approval workflows, workspace restrictions
  • Hooks & Events: Extension points for intercepting operations
  • Cache Management: Programmatic cache clearing
  • Error Handling: Proper error checking patterns
  • CLI Support: Using DataHandler in Symfony commands

Installation

Cursor (This Project)

The skill is already installed at .cursor/skills/typo3-datahandler-skill/.

A Cursor rule file is also available at .cursor/rules/typo3-datahandler.mdc for automatic activation when working with PHP files.

Claude Code

Option 1: Project-specific (Recommended)

Copy the skill folder to your project:

mkdir -p .claude/skills
cp -r .cursor/skills/typo3-datahandler-skill .claude/skills/

Option 2: Global Installation

Install globally for all projects:

mkdir -p ~/.claude/skills
cp -r .cursor/skills/typo3-datahandler-skill ~/.claude/skills/

Option 3: From Repository

# Clone and install
git clone https://github.com/dirnbauer/typo3-datahandler-skill.git ~/.claude/skills/typo3-datahandler-skill

Usage

This skill is automatically triggered when:

  • Working with TYPO3 record operations (create, update, delete)
  • Implementing DataHandler hooks or events
  • Writing CLI commands that modify database records
  • Copying/moving pages or content
  • Implementing localization features
  • Managing cache programmatically

Example Queries

  • "Create a new page with DataHandler"
  • "How do I hook into record save operations?"
  • "Copy a content element to another page"
  • "Delete records programmatically in TYPO3"
  • "Use DataHandler in a Symfony command"
  • "Publish workspace changes programmatically"
  • "How do workspaces affect DataHandler operations?"
  • "Set approval stage for workspace records"

Structure

typo3-datahandler-skill/
├── SKILL.md                    # Main skill file with core patterns
├── README.md                   # This file
└── references/
    ├── data-array.md           # Data array syntax and examples
    ├── command-array.md        # Command operations reference
    ├── workspaces.md           # Workspaces, versioning, staging, publishing
    ├── hooks-events.md         # DataHandler extension points
    └── common-patterns.md      # Real-world usage patterns

Quick Reference

Basic Usage

use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;

// Always create new instance
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($data, $cmd);
$dataHandler->process_datamap();
$dataHandler->process_cmdmap();

// Check errors
if ($dataHandler->errorLog !== []) {
    // Handle errors
}

Create Record

$data['pages']['NEW_page'] = [
    'pid' => 1,
    'title' => 'New Page',
];

Update Record

$data['pages'][42] = [
    'title' => 'Updated Title',
];

Delete Record

$cmd['pages'][42]['delete'] = 1;

Copy Record

$cmd['pages'][42]['copy'] = 10;  // To page 10
// After process_cmdmap():
$newUid = $dataHandler->copyMappingArray_merged['pages'][42];

Documentation

License

MIT License - See LICENSE for details.

Credits

Inspired by Netresearch's PHP Modernization Skill and the Anthropic Agent Skills specification.