Examples demonstrating the ctrl section of TCA 

The conference table of the conference extension has a configuration such as this:

EXT:my_extension/Configuration/TCA/tx_myextension_conference.php
<?php

return [
  'ctrl' => [
    'title' => 'my_extension.db:conference',
    'label' => 'title',
    'descriptionColumn' => 'internal_notes',
    'default_sortby' => 'conference_date DESC',
    'tstamp' => 'tstamp',
    'crdate' => 'crdate',
    'delete' => 'deleted',
    'editlock' => 'editlock',
    'enablecolumns' => [
      'disabled' => 'hidden',
      'starttime' => 'starttime',
      'endtime' => 'endtime',
      'fe_group' => 'fe_group',
    ],
    'languageField' => 'sys_language_uid',
    'transOrigPointerField' => 'l10n_parent',
    'transOrigDiffSourceField' => 'l10n_diffsource',
    'translationSource' => 'l10n_source',
    'versioningWS' => true,
    'origUid' => 't3_origuid',
    'iconfile' => 'EXT:my_extension/Resources/Public/Icons/Conference.svg',
    'security' => [
      'ignorePageTypeRestriction' => true,
    ],
  ],
  'columns' => [
    'title' => [
      'label' => 'my_extension.db:conference.title',
      'config' => [
        'type' => 'input',
        'required' => true,
        'max' => 255,
        'eval' => 'trim',
      ],
    ],
    'conference_date' => [
      'label' => 'my_extension.db:conference.conference_date',
      'config' => [
        'type' => 'datetime',
        'format' => 'date',
        'required' => true,
      ],
    ],
    'internal_notes' => [
      'label' => 'my_extension.db:conference.internal_notes',
      'config' => [
        'type' => 'text',
        'rows' => 3,
      ],
    ],
  ],
  // The fields of the other tabs are added in Configuration/TCA/Overrides/
  'types' => [
    '0' => [
      'showitem' => '
        title, conference_date,
        --div--;my_extension.db:tab.program,
        --div--;my_extension.db:tab.details,
        --div--;core.form.tabs:categories,
        --div--;core.form.tabs:access,
          hidden, --palette--;;access, fe_group, editlock,
        --div--;core.form.tabs:language,
          --palette--;;language,
        --div--;my_extension.db:tab.comments,
        --div--;core.form.tabs:notes,
          internal_notes,
      ',
    ],
  ],
  'palettes' => [
    'access' => [
      'label' => 'core.form.palettes:access',
      // The labels of the fields tell the editors what the dates are for
      'showitem' => '
        starttime;my_extension.db:conference.starttime,
        endtime;my_extension.db:conference.endtime,
      ',
    ],
    'language' => [
      'showitem' => 'sys_language_uid, l10n_parent',
    ],
  ],
];
Copied!

Minimal table configuration 

The sponsors of the conference extension have a small ctrl section:

EXT:my_extension/Configuration/TCA/tx_myextension_sponsor.php
<?php

return [
  'ctrl' => [
    'title' => 'my_extension.db:sponsor',
    'label' => 'name',
    'default_sortby' => 'name',
    'tstamp' => 'tstamp',
    'crdate' => 'crdate',
    'delete' => 'deleted',
    'iconfile' => 'EXT:my_extension/Resources/Public/Icons/Sponsor.svg',
  ],
  'columns' => [
    'name' => [
      'label' => 'my_extension.db:sponsor.name',
      'config' => [
        'type' => 'input',
        'required' => true,
      ],
    ],
  ],
  'types' => [
    '0' => [
      'showitem' => 'name',
    ],
  ],
];
Copied!

Property label is a mandatory setting, title and iconfile are a recommended minimum. The Content > Records module shows the icon and the translated title of the table, and it uses the value of the field name as title for single rows:

The sponsors in the Content > Records module

The sponsors in the Content > Records module

The sponsors additionally sort by name, keep deleted records in the database, and store when a record was created and changed. Single record administration is still limited with this setup: The sponsors can not be sorted manually, hidden, or translated. TYPO3 creates all database columns from the TCA.

Core table tt_content 

Table tt_content makes much more excessive use of the ['ctrl'] section. Find its current configuration in the file EXT:frontend/Configuration/TCA/tt_content.php.

A few remarks:

  • When tt_content records are displayed in the backend, the "label" property indicates that you will see the content from the field named "header" shown as the title of the record. If that field is empty, the content of field subheader and if empty, of field bodytext is used as title.
  • The field called "sorting" will be used to determine the order in which tt_content records are displayed within each branch of the page tree.
  • The title for the table as shown in the backend is defined as coming from a "locallang" file.
  • The "type" field will be the one named "CType". The value of this field determines the set of fields shown in the edit forms in the backend, see the ['types'] section for details.
  • Of particular note is the "enablecolumns" property. It is quite extensive for this table since it is a frontend-related table. Thus proper access rights, publications dates, etc. must be enforced.
  • Every type of content element has its own icon and its own class, used in conjunction with the Icon API to visually represent that type in the TYPO3 backend.

Extended container examples 

Disable a built-in wizard 

The inlineControlContainer renders the field wizard localizationStateSelector by default. In a translation, it lets editors choose whether an inline field with allowLanguageSynchronization follows the default language. Set container.inlineControlContainer.fieldWizard.localizationStateSelector.disabled to true in the ctrl section of a table to hide the wizard for all inline fields of that table.

Add your own wizard 

The form of a conference shows how many records refer to the conference. The FormWrapContainer renders this wizard above the fields:

The number of records that refer to a conference above its form

The number of records that refer to a conference above its form

Register the node in the file ext_localconf.php:

EXT:my_extension/ext_localconf.php
<?php

use MyVendor\MyExtension\Controller\ConferenceController;
use MyVendor\MyExtension\Evaluation\AbstractEvaluation;
use MyVendor\MyExtension\Evaluation\HashtagEvaluation;
use MyVendor\MyExtension\Form\Element\BadgeNameElement;
use MyVendor\MyExtension\Form\FieldInformation\InformationText;
use MyVendor\MyExtension\Form\FieldInformation\TalkOrderInformation;
use MyVendor\MyExtension\Form\FieldWizard\ReferencesToThisRecordWizard;
use TYPO3\CMS\Core\PasswordPolicy\Generator\PasswordGenerator;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;

defined('TYPO3') or die();

ExtensionUtility::configurePlugin(
  'MyExtension',
  'ConferenceList',
  [ConferenceController::class => ['list', 'show']],
);

// Classes that TCA fields name in 'eval'
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][HashtagEvaluation::class] = '';
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][AbstractEvaluation::class] = '';

// The information above the talks of a conference, see
// Configuration/TCA/Overrides/120-tx_myextension_conference-talks.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1791234567] = [
  'nodeName' => 'talkOrderInformation',
  'priority' => 30,
  'class' => TalkOrderInformation::class,
];

// The badge name of a frontend user, see
// Configuration/TCA/Overrides/650-fe_users-tx_myextension_badge_name.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1791300000] = [
  'nodeName' => 'badgeName',
  'priority' => 40,
  'class' => BadgeNameElement::class,
];

// The information text of fields, see for example
// Configuration/TCA/Overrides/310-tx_myextension_speaker-email.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1791310000] = [
  'nodeName' => 'informationText',
  'priority' => 30,
  'class' => InformationText::class,
];

// The references to a conference above its form, see
// Configuration/TCA/Overrides/101-tx_myextension_conference-references.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1791320000] = [
  'nodeName' => 'referencesToThisRecord',
  'priority' => 30,
  'class' => ReferencesToThisRecordWizard::class,
];

// The password policies of the password generators, see
// Configuration/TCA/Overrides/445-tx_myextension_location-door_code.php and
// Configuration/TCA/Overrides/345-tx_myextension_speaker-upload_token.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']['myExtensionDigits'] = [
  'generator' => [
    'className' => PasswordGenerator::class,
    'options' => [
      'length' => 8,
      'lowerCaseCharacters' => false,
      'upperCaseCharacters' => false,
      'digitCharacters' => true,
    ],
  ],
  'validators' => [],
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies']['myExtensionBase64'] = [
  'generator' => [
    'className' => PasswordGenerator::class,
    'options' => [
      'length' => 35,
      'random' => 'base64',
    ],
  ],
  'validators' => [],
];
Copied!

Add the node as a fieldWizard of the formWrapContainer:

EXT:my_extension/Configuration/TCA/Overrides/101-tx_myextension_conference-references.php
<?php

defined('TYPO3') or die();

// Above the form of a conference, show how many records refer to it
$GLOBALS['TCA']['tx_myextension_conference']['ctrl']['container']['formWrapContainer']
  ['fieldWizard']['referencesToThisRecord'] = [
    // Registered in ext_localconf.php
    'renderType' => 'referencesToThisRecord',
  ];
Copied!

The node extends AbstractNode and returns its HTML in the result array:

EXT:my_extension/Classes/Form/FieldWizard/ReferencesToThisRecordWizard.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Form\FieldWizard;

use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Core\Database\ReferenceIndex;
use TYPO3\CMS\Core\Localization\LanguageService;

/**
 * Shows above the form how many records refer to the edited record
 */
final class ReferencesToThisRecordWizard extends AbstractNode
{
  public function __construct(
    private readonly ReferenceIndex $referenceIndex,
  ) {}

  public function render(): array
  {
    $result = $this->initializeResultArray();
    $uid = (int)($this->data['databaseRow']['uid'] ?? 0);
    if ($uid === 0) {
      // A new record has no references yet
      return $result;
    }
    $count = $this->referenceIndex->getNumberOfReferencedRecords($this->data['tableName'], $uid);
    $result['html'] = '<p class="text-body-secondary">' . htmlspecialchars(sprintf(
      $this->getLanguageService()->sL('my_extension.db:references'),
      $count,
    )) . '</p>';
    return $result;
  }

  private function getLanguageService(): LanguageService
  {
    return $GLOBALS['LANG'];
  }
}
Copied!

Add fieldInformation to field of type inline 

This example can be found in the example with a custom fieldInformation.