Examples 

Simple 1:n relation 

A conference has talks, and each talk belongs to one conference:

The talks of a conference

The talks of a conference

EXT:my_extension/Configuration/TCA/Overrides/120-tx_myextension_conference-talks.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'talks' => [
    'label' => 'my_extension.db:conference.talks',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tx_myextension_talk',
      'foreign_field' => 'conference',
      'foreign_sortby' => 'sorting',
      'appearance' => [
        'collapseAll' => true,
        'useSortable' => true,
        'showPossibleLocalizationRecords' => true,
        'showAllLocalizationLink' => true,
        'showSynchronizationLink' => true,
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'talks',
  '',
  'before:--div--;my_extension.db:tab.details',
);

// An inline field has no fieldInformation of its own. The container of all
// inline fields of the table shows it, see TalkOrderInformation.
$GLOBALS['TCA']['tx_myextension_conference']['ctrl']['container']['inline']
  ['fieldInformation']['talkOrder'] = [
    'renderType' => 'talkOrderInformation',
  ];
Copied!

Attributes on anti-symmetric intermediate table 

The registrations of a conference

The registrations of a conference

This example combines conferences with frontend users, using the intermediate table tx_myextension_registration. Each registration has an attribute of its own: the ticket of the attendee.

The conference contains the following column:

EXT:my_extension/Configuration/TCA/Overrides/126-tx_myextension_conference-registrations.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'registrations' => [
    'label' => 'my_extension.db:conference.registrations',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tx_myextension_registration',
      'foreign_field' => 'conference',
      'foreign_selector' => 'attendee',
      'foreign_unique' => 'attendee',
      'foreign_match_fields' => [
        'status' => 'confirmed',
      ],
      'appearance' => [
        'useCombination' => true,
        'overwriteCombinationWarningMessage' => 'my_extension.db:conference.registrations.combinationWarning',
      ],
      'overrideChildTca' => [
        'columns' => [
          'attendee' => [
            'config' => [
              // The frontend users are stored in the same folder as the conferences
              'elementBrowserEntryPoints' => [
                'fe_users' => '###CURRENT_PID###',
              ],
            ],
          ],
        ],
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  '--div--;my_extension.db:tab.registrations, registrations',
  '',
  'before:--div--;my_extension.db:tab.details',
);
Copied!

The intermediate table tx_myextension_registration defines the following fields:

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

return [
  'ctrl' => [
    'title' => 'my_extension.db:registration',
    'label' => 'attendee',
    'label_alt' => 'ticket',
    'label_alt_force' => true,
    'hideTable' => true,
    'tstamp' => 'tstamp',
    'crdate' => 'crdate',
    'delete' => 'deleted',
    'versioningWS' => true,
    'iconfile' => 'EXT:my_extension/Resources/Public/Icons/Registration.svg',
  ],
  'columns' => [
    'attendee' => [
      'label' => 'my_extension.db:registration.attendee',
      'config' => [
        'type' => 'group',
        'allowed' => 'fe_users',
        'minitems' => 1,
        'maxitems' => 1,
      ],
    ],
    'ticket' => [
      'label' => 'my_extension.db:registration.ticket',
      'config' => [
        'type' => 'select',
        'renderType' => 'selectSingle',
        'items' => [
          ['label' => 'my_extension.db:registration.ticket.regular', 'value' => 'regular'],
          ['label' => 'my_extension.db:registration.ticket.student', 'value' => 'student'],
          ['label' => 'my_extension.db:registration.ticket.speaker', 'value' => 'speaker'],
        ],
      ],
    ],
  ],
  'types' => [
    '0' => [
      'showitem' => 'attendee, ticket',
    ],
  ],
];
Copied!

Attributes on symmetric intermediate table 

The partner conferences of a conference

The partner conferences of a conference

This example combines records of the same table with each other: a conference has partner conferences, whose attendees get a discount. Symmetric relations combine records of one table with each other. If record A is related to record B, then record B is also related to record A. However, the records are not stored in groups. If record A is related to B and C, B does not have to be related to C.

The conference has a field storing the inline relation, here: partners.

EXT:my_extension/Configuration/TCA/Overrides/128-tx_myextension_conference-partners.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'partners' => [
    'label' => 'my_extension.db:conference.partners',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tx_myextension_partnership',
      'foreign_field' => 'conference',
      'foreign_sortby' => 'conference_sorting',
      'foreign_label' => 'partner',
      'symmetric_field' => 'partner',
      'symmetric_sortby' => 'partner_sorting',
      'symmetric_label' => 'conference',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'partners',
  '',
  'after:speakers',
);
Copied!

The conferences are related to each other with the intermediate table tx_myextension_partnership. It stores the uids of both sides of the relation in conference and partner:

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

return [
  'ctrl' => [
    'title' => 'my_extension.db:partnership',
    'label' => 'partner',
    'hideTable' => true,
    'tstamp' => 'tstamp',
    'crdate' => 'crdate',
    'delete' => 'deleted',
    'versioningWS' => true,
    'iconfile' => 'EXT:my_extension/Resources/Public/Icons/Partnership.svg',
  ],
  'columns' => [
    'conference' => [
      'label' => 'my_extension.db:partnership.conference',
      'config' => [
        'type' => 'select',
        'renderType' => 'selectSingle',
        'foreign_table' => 'tx_myextension_conference',
        'foreign_table_where' => 'AND {#tx_myextension_conference}.{#sys_language_uid} IN (-1, 0)',
      ],
    ],
    'partner' => [
      'label' => 'my_extension.db:partnership.partner',
      'config' => [
        'type' => 'select',
        'renderType' => 'selectSingle',
        'foreign_table' => 'tx_myextension_conference',
        'foreign_table_where' => 'AND {#tx_myextension_conference}.{#sys_language_uid} IN (-1, 0)',
      ],
    ],
  ],
  'types' => [
    '0' => [
      'showitem' => 'conference, partner',
    ],
  ],
];
Copied!

Each side of the relation has its own sorting. TYPO3 does not create the columns of foreign_sortby and symmetric_sortby, so they are defined in the ext_tables.sql file:

EXT:my_extension/ext_tables.sql
-- The sorting of each side of a partnership. TYPO3 creates all other
-- columns from the TCA.
CREATE TABLE tx_myextension_partnership (
  conference_sorting int(11) unsigned DEFAULT '0' NOT NULL,
  partner_sorting int(11) unsigned DEFAULT '0' NOT NULL
);

-- TYPO3 does not create columns for fields of type user
CREATE TABLE fe_users (
  tx_myextension_badge_name varchar(255) DEFAULT '' NOT NULL
);

-- TYPO3 does not create columns for fields of type none
CREATE TABLE tx_myextension_talk (
  feedback_opened int(11) DEFAULT '0' NOT NULL,
  rating decimal(2,1) DEFAULT '0.0' NOT NULL,
  feedback_count int(11) unsigned DEFAULT '0' NOT NULL
);
Copied!

With a combination box 

The registrations of a conference show the registration and the frontend user together. A message warns that changes to the frontend user apply to all registrations of this user:

A registration with its frontend user

A registration with its frontend user

EXT:my_extension/Configuration/TCA/Overrides/126-tx_myextension_conference-registrations.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'registrations' => [
    'label' => 'my_extension.db:conference.registrations',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tx_myextension_registration',
      'foreign_field' => 'conference',
      'foreign_selector' => 'attendee',
      'foreign_unique' => 'attendee',
      'foreign_match_fields' => [
        'status' => 'confirmed',
      ],
      'appearance' => [
        'useCombination' => true,
        'overwriteCombinationWarningMessage' => 'my_extension.db:conference.registrations.combinationWarning',
      ],
      'overrideChildTca' => [
        'columns' => [
          'attendee' => [
            'config' => [
              // The frontend users are stored in the same folder as the conferences
              'elementBrowserEntryPoints' => [
                'fe_users' => '###CURRENT_PID###',
              ],
            ],
          ],
        ],
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  '--div--;my_extension.db:tab.registrations, registrations',
  '',
  'before:--div--;my_extension.db:tab.details',
);
Copied!

Add a custom fieldInformation 

The following example adds a custom fieldInformation above the talks of a conference. Adding a fieldWizard is done in a similar way.

The talks of a conference with the information about their order

The talks of a conference with the information about their order

As explained in the description, fieldInformation or fieldWizard must be configured within the ctrl for the field type inline - as it is a container.

  1. Create a custom fieldInformation

    EXT:my_extension/Classes/Form/FieldInformation/TalkOrderInformation.php
    <?php
    
    declare(strict_types=1);
    
    namespace MyVendor\MyExtension\Form\FieldInformation;
    
    use TYPO3\CMS\Backend\Form\AbstractNode;
    use TYPO3\CMS\Core\Localization\LanguageService;
    
    /**
     * Explains above the talks of a conference how to change the program
     */
    final class TalkOrderInformation extends AbstractNode
    {
      public function render(): array
      {
        $result = $this->initializeResultArray();
        // The container shows this information for every inline field of the table
        if ($this->data['fieldName'] !== 'talks') {
          return $result;
        }
        $result['html'] = '<p class="text-body-secondary">' . htmlspecialchars(
          $this->getLanguageService()->sL('my_extension.db:conference.talks.order'),
        ) . '</p>';
        return $result;
      }
    
      private function getLanguageService(): LanguageService
      {
        return $GLOBALS['LANG'];
      }
    }
    
    Copied!
  2. Register this node type

    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!
  3. Add the fieldInformation to the container for containerRenderType inline

    EXT:my_extension/Configuration/TCA/Overrides/120-tx_myextension_conference-talks.php
    <?php
    
    use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
    
    defined('TYPO3') or die();
    
    ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
      'talks' => [
        'label' => 'my_extension.db:conference.talks',
        'config' => [
          'type' => 'inline',
          'foreign_table' => 'tx_myextension_talk',
          'foreign_field' => 'conference',
          'foreign_sortby' => 'sorting',
          'appearance' => [
            'collapseAll' => true,
            'useSortable' => true,
            'showPossibleLocalizationRecords' => true,
            'showAllLocalizationLink' => true,
            'showSynchronizationLink' => true,
          ],
        ],
      ],
    ]);
    ExtensionManagementUtility::addToAllTCAtypes(
      'tx_myextension_conference',
      'talks',
      '',
      'before:--div--;my_extension.db:tab.details',
    );
    
    // An inline field has no fieldInformation of its own. The container of all
    // inline fields of the table shows it, see TalkOrderInformation.
    $GLOBALS['TCA']['tx_myextension_conference']['ctrl']['container']['inline']
      ['fieldInformation']['talkOrder'] = [
        'renderType' => 'talkOrderInformation',
      ];
    
    Copied!

Examples with overrideChildTca 

Overrides the crop variants 

This example overrides the crop variants of the photo of a speaker. The photo can only be cropped to a square:

EXT:my_extension/Configuration/TCA/Overrides/320-tx_myextension_speaker-photo.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_speaker', [
  'photo' => [
    'label' => 'my_extension.db:speaker.photo',
    'config' => [
      'type' => 'file',
      'maxitems' => 1,
      'allowed' => 'common-image-types',
      'relationship' => 'manyToOne',
      'overrideChildTca' => [
        'columns' => [
          'crop' => [
            'config' => [
              'cropVariants' => [
                'default' => [
                  'title' => 'my_extension.db:speaker.photo.square',
                  'allowedAspectRatios' => [
                    '1:1' => [
                      'title' => 'core.wizards:imwizard.ratio.1_1',
                      'value' => 1.0,
                    ],
                  ],
                  // The face of the speaker, initially in the middle
                  'focusArea' => [
                    'x' => 1 / 3,
                    'y' => 1 / 3,
                    'width' => 1 / 3,
                    'height' => 1 / 3,
                  ],
                ],
              ],
            ],
          ],
        ],
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_speaker',
  'photo',
  '',
  'before:--div--;my_extension.db:tab.integration',
);
Copied!

Define which fields to show in the child table 

This example overrides the showitem field of the child table TCA:

The content elements of a conference

The content elements of a conference

EXT:my_extension/Configuration/TCA/Overrides/159-tx_myextension_conference-content_elements.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'content_elements' => [
    'label' => 'my_extension.db:conference.content_elements',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tt_content',
      'foreign_field' => 'tx_myextension_conference',
      'behaviour' => [
        'allowLanguageSynchronization' => true,
      ],
      'overrideChildTca' => [
        'columns' => [
          'CType' => [
            'config' => [
              'default' => 'text',
            ],
          ],
        ],
        'types' => [
          'text' => [
            'showitem' => 'CType, header, bodytext',
          ],
        ],
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'content_elements',
  '',
  'before:--div--;core.form.tabs:categories',
);
Copied!

Override the default value of a child tables field 

This overrides the default columns property of a child field in an inline relation from within the parent if a new child is created:

EXT:my_extension/Configuration/TCA/Overrides/159-tx_myextension_conference-content_elements.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'content_elements' => [
    'label' => 'my_extension.db:conference.content_elements',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tt_content',
      'foreign_field' => 'tx_myextension_conference',
      'behaviour' => [
        'allowLanguageSynchronization' => true,
      ],
      'overrideChildTca' => [
        'columns' => [
          'CType' => [
            'config' => [
              'default' => 'text',
            ],
          ],
        ],
        'types' => [
          'text' => [
            'showitem' => 'CType, header, bodytext',
          ],
        ],
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'content_elements',
  '',
  'before:--div--;core.form.tabs:categories',
);
Copied!

Override the foreign_selector field target 

This overrides the configuration of the field the foreign_selector property points to. Here, the element browser of the attendees of a conference opens on the folder of the conference:

EXT:my_extension/Configuration/TCA/Overrides/126-tx_myextension_conference-registrations.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'registrations' => [
    'label' => 'my_extension.db:conference.registrations',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tx_myextension_registration',
      'foreign_field' => 'conference',
      'foreign_selector' => 'attendee',
      'foreign_unique' => 'attendee',
      'foreign_match_fields' => [
        'status' => 'confirmed',
      ],
      'appearance' => [
        'useCombination' => true,
        'overwriteCombinationWarningMessage' => 'my_extension.db:conference.registrations.combinationWarning',
      ],
      'overrideChildTca' => [
        'columns' => [
          'attendee' => [
            'config' => [
              // The frontend users are stored in the same folder as the conferences
              'elementBrowserEntryPoints' => [
                'fe_users' => '###CURRENT_PID###',
              ],
            ],
          ],
        ],
      ],
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  '--div--;my_extension.db:tab.registrations, registrations',
  '',
  'before:--div--;my_extension.db:tab.details',
);
Copied!

Example: Override by type 

A record type can change the overrideChildTca of an inline field with columnsOverrides. Here, the editors cannot change the text of a comment on a talk of the record type keynote:

EXT:my_extension/Configuration/TCA/Overrides/255-tx_myextension_talk-comments.php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
  'comments' => [
    'label' => 'my_extension.db:talk.comments',
    'config' => [
      'type' => 'inline',
      'foreign_table' => 'tx_myextension_comment',
      'foreign_field' => 'parent',
      'foreign_table_field' => 'parent_table',
      'appearance' => [
        'enabledControls' => [
          'new' => false,
        ],
      ],
    ],
  ],
]);
// The website quotes the comments on a keynote: the editors approve them,
// but they cannot change their text
$GLOBALS['TCA']['tx_myextension_talk']['types']['keynote']['columnsOverrides']['comments']['config']['overrideChildTca']['columns']['content']['config']['readOnly'] = true;
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_talk',
  'comments',
  '',
  'before:--div--;core.form.tabs:access',
);
Copied!