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.
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.
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.
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.
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
Let's move on.
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
group. By setting the
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.
'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
true being the default, obviously). This will have no effect on
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!
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.
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
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
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
common-media-types: This is a placeholder for
common-text-types: This is a placeholder for
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
behaviour configuration options are still the same as they were in the
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
This concludes the new TCA type
file, that allows for a more convenient way to register our fields for FAL relation.
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
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
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
Note, that setting
false will disable the whole link browser (with
true being the default, obviously).
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
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.
'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
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
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
But there is more.
With the TCA type
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
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
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.
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
Alright, whats next?
'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!
'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.
For fields that we used to configure with type
internal_type => folder, we now have a dedicated type:
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
true, the form engine will ... you guessed it .. hide the move icons that let us change the order of the selected folders.
- New TCA Types in version 11, 2023, Blog Post by Oliver Bartsch
- TCA Reference, Official Documentation
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