You too can become a hero and support this Blog at GitHub Sponsors or Patreon.

More about support

The beloved Table Configuration Array, better known by its abbreviation "TCA", is the glue between the TYPO3 CMS and the database. In the TCA we configure each database table in terms of its behavior, visibility, restrictions and editing interface (aka form engine). It is a central piece of every TYPO3 system and thus has received a lot of love recently.

For example, the amount of render types and eval options for input fields had been reduced by replacing them with new and unique TCA types. Additionally, some clunky configuration options have been streamlined and also moved to their dedicated new types.

Kudos go to all involved developers, especially to Oliver Bartsch (say Thanks on Twitter) who did a lot of the work in this area, as well as to the TYPO3 documentation team.

Before we look at the newly introduced TCA types in detail and learn how to use them, we will have a quick detour through the goodness that is EXT:styleguide.

Buckle up, let's go.

EXT:styleguide

A quick word on how to get an idea of the state of the TCA feature set for current TYPO3 main branch or any released LTS version:

Use the extension styleguide (GitHub).

So, now you know. Let's carry on.

OK, seriously, EXT:styleguide is the way to go. It is a dev-dependency for the TYPO3 core for a long time and is kept up to date with new features and changes in several areas of the TYPO3 core, one of them being the TCA. With the click of a button, the extension will generate a page tree with example records for every TCA configuration that TYPO3 is capable of, including the new types we are discussing in this post.

In fact, some code examples and screenshots in this post are taken from the styleguide extension example records, because, ... well, they are good examples.

Check it out, if you have not done so yet.

Additionally, there is the quite extensive and well structured official documentation, called TCA Reference. I'll add a link to the documentation for each of the features. If the documentation contradicts this blog post, stick to the documentation and let me know.

With this out of the way, let's dive in.

New in TYPO3 11

With TYPO3 v11, two often needed configuration cases have been addressed, that had a less than ideal solution in older versions. With streamlined TCA types, the configuration of language and category fields became a lot more intuitive.

You can also check out Oli Bartschs article New TCA types in TYPO3 version 11 about this topic over at the b13 blog.

Let's have a closer look.

Top

Language

Since TYPO3 11

Patch | Feature RST | Official Documentation

In TYPO3 many records are translatable and the corresponding database tables have a field configured to store the language (id). That is what the [ctrl][languageField] configuration option of the tables TCA is for. Usually the field for storing the language is called sys_language_uid but it could be any other field as well.

In TYPO3 versions prior to v11 the corresponding field needed to have a column configuration specifying a relation to the sys_language database table (type: select) where all system languages used to be stored.

With the introduction of site handling in TYPO3 v9 a new place to define existing languages had been introduced: site configuration.

Going forward site configurations will be the only place where languages are defined. That is why the whole concept of the sys_language as well as the database table itself has been deprecated with TYPO3 v11 (Patch and Deprecation RST) and has been removed with TYPO3 v12 (Patch).

Long story short: for language fields of our tables we have to use the following TCA configuration from now on:

'columns' => [
	'sys_language_uid' => [
		'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
		'config' => [
			'type' => 'language',
		],
	],
],

The new TCA type automatically takes care of collecting all languages from the site configurations as well as handling the -1 special case (that is not available for pages). Overriding with PageTsConfig is still possible.

There also is a TCA migration in place to remind us in the deprecation log to migrate any legacy column configuration to the new type.

Top

Category

Since TYPO3 11

Patch | Feature RST | Official Documentation

Prior to TYPO3 v11 we had to call ExtensionManagementUtility->makeCategorizable() to add the system categories selection tree to our records. With TYPO3 v11 a simpler way of making a table categorizable has been introduced: The category TCA type.

All we have to do to add categorization is to configure one column to be of the new type:

'columns' => [
	'categories' => [
		'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.category',
		'config' => [
			'type' => 'category',
		 // 'relationship' => 'manyToMany' | 'oneToMany' | 'oneToOne'
		],
	],
],

By default, this sets up a manyToMany relationship, meaning that we can select as many categories for our record as we like and the relation is stored in the sys_category_record_mm relation table. To change this behavior we can set the relationship option to either:

  • oneToMany: We can still select as many categories as we like, but the uids of the selected categories are stored as a comma separated list in our database field.
  • oneToOne: We can only select one category and the category uid is stored in our database field.
  • manyToMany: The default. See above.

With the new and more intutitive way in place to register a table for categorization, the before used CategoryRegistry had been deprecated with TYPO3 v11 (Patch, Deprecation RST) and was removed with TYPO3 v12 (Patch). It also enabled quite some refactoring (check out the patch) to drastically improve performance of the category tree.

Fields configured with the TCA type category will be automatically created in the database and can be omitted from ext_tables.sql files.

Let's move on.

Top

New in TYPO3 12

With TYPO3 v12 the focus shifted to the multitude of different field behaviors that used to be possible with the all star type input, but also with types inline and group. By setting the renderType, specific eval values or other special options, the parent types behavior could change drastically.

The overall idea of the streamlining during TYPO3 v12 development was to get rid of many special cases and introduce dedicated TCA types for each of them, so that the usage becomes more clear and intuitive.

Let's see what has been achieved.

Top

Password

Since TYPO3 12

Patch | Feature RST | Official Documentation

Instead of configuring a field of type input with an eval like trim,password,saltedPassword, we can now simply use the new password type:

'columns' => [
  'password' => [
    'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:password',
    'config' => [
    	'type' => 'password',
	 // 'hashed' => true | false,
    ],
  ],
],

With this most basic configuration we'll get an input field that will be concealed once saved and store a hash of the submitted input value. The database entry will only be the plaintext value if we set hashed explicitly to false (with true being the default, obviously). This will have no effect on fe_users or be_users tables and in general it is quite hard to come up with a use case where this would be a good idea.

Do not store plaintext passwords in the database!

Just don't.

However, the password type is capable of much more. Let's look at a full-blown configuration (borrowed from the styleguide Extension):

'columns' => [
  'password_5' => [
	'label' => 'password_5',
	'description' => 'type=password fieldControl=passwordGenerator random=base64 allowEdit=false',
	'config' => [
		'type' => 'password',
			'fieldControl' => [
				'passwordGenerator' => [
					'renderType' => 'passwordGenerator',
					'options' => [
						'title' => 'Create random base64 string',
						'allowEdit' => false,
						'passwordRules' => [
							'length' => 35,
							'random' => 'base64',
						],
					],
				],
			],
		],
	],
],

Notably, this configuration uses the [fieldControl][passwordGenerator], that has been introduced with TYPO3 12.1 (Patch, Feature RST). This feature is also used by the new system extension EXT:reactions (read my article Webhooks and Reactions in TYPO3 to find out more).

The password generator adds a button next to the input field that will generate a random string according to the configuration. In this case we'll get a read only field with 35 characters from base64 random bytes. It would look like this


The official documentation of the password generator field control feature can be found here.

What's next?

Top

File

Since TYPO3 12

Patch | Feature RST | Official Documentation

Adding a file relation to our records is a common use case. In TYPO3 versions prior to v12, we had to use the TCA type inline and set the foreign_table to sys_file_reference and then configure it further. There has been an API in place to help with that (ExtensionManagementUtility->getFileFieldTCAConfig()). All of this is now obsolete because we got the TCA type file.

Here is how it looks:

'columns' => [
    'images' => [
        'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:images',
        'config' => [
            'type' => 'file',
            'maxitems' => 6,
         // 'allowed' => 'common-image-types' | 'common-media-types' | 'common-text-types'
		    'allowed' => ['jpg','youtube'],
		 // 'disallowed' => ['gif'],
			'appearance' => [] 	// same as before
			'behaviour' => [] 	// same as before
        ],
    ],
],

The first thing that we notice is the option allowed. This can be set to any array of file and media extensions or to one of the three presets:

  • common-image-types: This is a placeholder for $GLOBALS[TYPO3_CONF_VARS][GFX][imagefile_ext]
  • common-media-types: This is a placeholder for $GLOBALS[TYPO3_CONF_VARS][SYS][mediafile_ext]
  • common-text-types: This is a placeholder for $GLOBALS[TYPO3_CONF_VARS][SYS][textfile_ext]

When using one of the presets we can also configure the disallowed option to exclude file and media extensions.

The above configuration will lead to field that looks like this:


Of course, we can still determine what buttons should be available, as well as their labels and behavior, we can also still use overrideChildTca to adapt the appearance of the sys_file_reference fields. The appearance and behaviour configuration options are still the same as they were in the inline type.

To manipulate the controls, three new PSR-14 Events (read my post about PSR-14 Events in TYPO3 for an overview) have been added to the processing of the TCA type file (Patch, Feature RST). We have to make sure to use the new Events instead of the old TCA type inline hook, that we might have used before, because this hook is not evaluated for the file type.

This concludes the new TCA type file, that allows for a more convenient way to register our fields for FAL relation.

Top

Link

Since TYPO3 12

Patch | Feature RST | Official Documentation

A good example of when the TCA type input was used for an out-of-scope functionality, would be the link popup. With renderType => inputLink and the configuration of the linkPopup wizard, an innocent input field turned into a mighty link generator.

To end this madness, the TCA type link has been introduced.

Here is an example configuration:

'columns' => [
	'link' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:link',
		'config' => [
			'type' => 'link',
			'allowedTypes' => ['page', 'url', 'record'],
			'appearance' => [
			 // 'enableBrowser' => false,
				'allowedFileExtensions' => ['jpg', 'png'],
				'allowedOptions' => ['*'],
			],
		],
	],
],

A few things of note here:

Going forward, the link browser can only be configured with allow lists, where prior to TYPO3 v12 disallow lists have been used. This affects the available link types, file extensions for file links and options for the generated <a> tags.

We now configure the available tabs of the link browser directly in the config array by setting the allowedTypes to any number of available link types (The core comes with page ,file, url, telephone and record). To enable all types, we can simply use ['*']. Also, the record type can be made more specific. While record enables all records, we could replace it with one or more specific record types, like for example tx_news.

Note, that setting appearance[enableBrowser] to false will disable the whole link browser (with true being the default, obviously).

Finally appearance[allowedFileExtensions] and appearance[allowedOptions] let us control what file extensions are available to link to (with enabled file link type) and what options are available for the generated <a> tag (the core comes with target, title, class, params and rel).

With that we should be able to have nice and working link fields in our records in TYPO3 v12 and beyond. Let's move on.

Top

Number

Since TYPO3 12

Patch | Feature RST | Official Documentation

To make the input type text-only, all integer based field functionality has been moved to the new TCA type number.  This includes everything that was formerly achieved by setting eval to int or double2 on fields of type input.

We now can do the following:

'columns' => [
	'number' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:number',
		'config' => [
			'type' => 'number',
		 // 'format' => 'decimal'
			'range' => [
            	'lower' => 1,
            	'upper' => 100
			],
		]
	],
],

This configuration leads to a basic input field that we can put numbers into, either with or without decimals, depending on the format configuration value. If we specify a range, the field will be validated before submit to ensure the input value lies within the lower and and upper limit.

So far, so simple.

The TCA type number is capable of two more things (that we already know from the former feature set of the input type):

  • slider:  This will add a little slider next to the input field (see screenshot below).
  • valuePicker: This will add a dropdown next to the input field, with predefined values to choose from (see screenshot below).

Let's look at a configuration for both of them:

'columns' => [
	'slider' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:numberWithSlider',
		'description' => 'slider default=14.5 step=0.5 width=150 format=decimal',
		'config' => [
			'type' => 'number',
			'default' => 14.5,
            'format' => 'decimal',
            'size' => 5,
			'slider' => [
				'step' => 0.5,
				'width' => 150,
			],
		],
	],

	'picker' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:numberWithPicker',
		'description' => 'value picker',
		'config' => [
			'type' => 'number',
			'valuePicker' => [
				'items' => [
					['One', '1'],
					['Two', '2'],
				],
			],
		],
	],
],

This configuration turns into the following fields:


That is everything there is to know about the TCA type number.

But there is more.

Top

Datetime

Since TYPO3 12

Patch | Feature RST | Official Documentation

With the TCA type datetime, another renderType of type input bites the dust.

Beginning with TYPO3 v12, all fields that are configured to be of type datetime will be automatically created in the database and can be omitted from ext_tables.sql files (the dbType option is explained below). However, if those fields are present in a ext_tables.sql, the definition there will take precedence.

Let's look at an example:

'columns' => [
	'start_date' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:startDate',
		'config' => [
			'type' => 'datetime',
			'dbType' => 'datetime' | 'date' | 'time',
			'format' => 'datetime' | 'date' | 'time',
		 // 'disableAgeDisplay' => true,
		 // 'nullable' => true,
		],
	],
],

To break this down, we first look at the configuration option dbType. If set, the value will not be stored as timestamp in the database, but as native date, time or datetime field. If the dbType option is not set, the value will be converted to a timestamp and stored as an integer in the database.

When we set nullable to true, the field can be disabled and stored as null in the database. Otherwise, a null value is not possible. The automatically created database column will reflect this setting.

The format option lets us configure how the date (or time) should be displayed in the backend. It is independent from the format in the database (that is what dbType is for).

Since the list module will by default calculate the "age" of a field by showing the difference of days for a date value to today, the option disableAgeDisplay has been introduced back in TYPO3 v7 (Feature RST). If we set this option to true, the calculation will be skipped to increase backend performance. It is recommended to always set this unless you need the additional "age" display in the list module.

Last but not least, a datetime field will always have a datepicker unless it is set to be readonly.

Alright, whats next?

Email

Since TYPO3 12

Patch | Feature RST | Official Documentation

This is a quick one. The new TCA type email replaces the eval => email option of type input. It is very straightforward:

'columns' => [
	'email' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:email',
		'config' => [
			'type' => 'email',
		],
	],
],

The value will be trimmed automatically and there is really nothing more to say about this type.

To the next one!

Top

Color

Since TYPO3 12

Patch | Feature RST | Official Documentation

The new TCA type color replaces yet another former render type of TCA type input (it was called colorpicker). It will create a field with a colorpicker. The configuration looks like this:

'columns' => [
	'color' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:color',
		'config' => [
			'type' => 'color',
		 // 'nullable' => true,
			'valuePicker' => [
            	'items' => [
        	        ['typo3 orange', '#FF8700'],
    	        ],
	        ],
      	],
	],
],

As shown in the example, the value picker is still available. The nullable option will add a checkbox next to the field to disable it and store null in the database field.

Straightforward again.

Almost done.

Top

Folder

Since TYPO3 12

Patch | Feature RST | Official Documentation

For fields that we used to configure with type group and internal_type => folder, we now have a dedicated type: folder.

There is, however, one more thing: We can determine the entry point of the folder browser with the new option elementBrowserEntryPoints that has also been introduced with TYPO3 12 (Patch, Feature RST).

A field configured with type folder could look like this:

'columns' => [
	'folder' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:folder',
		'config' => [
			'type' => 'folder',
			'elementBrowserEntryPoints' => [
				'_default' => '1:/my_folder/'
			 // '_default' => '###PAGE_TSCONFIG_ID###'
			 // 'hideMoveIcons' => false | true
          	],
      	],
	],
],

This will render a select box with a folder selection button next to it. The button will open the file browser at the configured entry point so that we can choose our folder(s).

If we set hideMoveIcons to true, the form engine will ... you guessed it .. hide the move icons that let us change the order of the selected folders.

Awesome.

Top

JSON

Since TYPO3 12

Patch | Feature RST | Official Documentation (pending)

This TCA type can be used for database fields containing JSON data. It will render the JSON in a text area field or if the system extension t3editor is installed and the configuration option enableCodeEditor is not explicitly set to false in a code editor field.

A field configuration could look like this:

'columns' => [
	'json' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:json',
		'config' => [
			'type' => 'json',
		//  'enableCodeEditor' => false,
		//  'readonly' => true,
      	],
	],
],

Not much to say about this one. Few things: enableCodeEditor defaults to true. We can also make the filed readonly, which might make sense, because most of the time JSON is not the ideal input format.

Top

UUID

Since TYPO3 12

Patch | Feature RST | Official Documentation

With the new uuid TCA type, we can generate an universally unique identifier for a database row. Under the hood, it uses the symfony component symfony/uid (documentation).

An example configuration:

'columns' => [
	'uuid' => [
		'label' => 'LLL:EXT:my_ext/Resources/Private/Language/locallang_tca.xlf:uuid',
		'config' => [
			'type' => 'uuid',
		    'version' => 7,
		//  'enableCopyToClipboard' => true,
      	],
	],
],

The form engine will initially generate a uuid according to the specified version (4, 6 or 7) if the field is configured with type uuid and contains no value yet. The field is then rendered as a read only field. Optionally, a copy to clipboard button can be enabled.

An example of this can be found in the new-to-v12 system extension EXT:reactions (read my article about reactions and webhooks).


This should make it quite easy to add uuids to our database rows.

Hooray!

Top

Additional Resources


That was a lot. Why bother, you might ask?

Well, I put this post together to have all the new stuff in one place that I can refer to and that I can (and will) update when new things are added. I also hope this collection of TCA goodies can be of use to other developers as well (If you are not like me and actually work with the newer versions of TYPO3, it very well might be).

Please let me know if something is missing or wrong.

And that's it - thanks for reading and (if you do) for supporting this blog <3.