Migrating an old extension can be a tough job, especially when the extension was developed before TYPO3 CMS 6.2 times and thus doesn't follow the current best practises. In this article I'll show you how to migrate the TCA placed in an ext_tables.php file of a real extension that was originally developed for TYPO3 4.5.

In the old times, literally everything was placed in ext_tables.php: TCA, content element wizards, TypoScript includes and a lot more. In this series I'll guide you how to clean up that mess into a proper stucture. Today we handle the Table Configuration Array, or "TCA" for short.

As already teased, this is a real world extension. However, the extension name is anonymized. No further cleanups were made.

The TCA is probably split into two files: ext_tables.php and a dedicated tca.php.

The part in ext_tables.php:

$TCA["my_extension_table"] = Array (
    "ctrl" => Array (
        'title' => 'LLL:EXT:my_extension/locallang_db.xml:my_extension_table',
        'label' => 'title',
        'tstamp' => 'tstamp',
        'crdate' => 'crdate',
        'cruser_id' => 'cruser_id',
        'versioningWS' => TRUE,
        'origUid' => 't3_origuid',
        "default_sortby" => "ORDER BY crdate DESC",
        "delete" => "deleted",
        "enablecolumns" => Array (
            "disabled" => "hidden",
            "starttime" => "starttime",
            "endtime" => "endtime",
            "fe_group" => "fe_group",
        "dynamicConfigFile" => t3lib_extMgm::extPath($_EXTKEY)."tca.php",
        "iconfile" => t3lib_extMgm::extRelPath($_EXTKEY)."icon_tx_myextension_table.gif",
        "dividers2tabs"  => 1,
        "canNotCollapse" => 1,
    "feInterface" => Array (
        "fe_admin_fieldList" => "hidden, starttime, endtime, fe_group, title, content",

The part in tca.php looks like this:

$TCA["tx_myextension_table"] = Array (
    "ctrl" => $TCA["tx_myextension_table"]["ctrl"],
    "interface" => Array (
        "showRecordFieldList" => "hidden,starttime,endtime,fe_group,title,content,info,version,edit_date,edit_user"
    "feInterface" => $TCA["tx_myextension_table"]["feInterface"],
    "columns" => Array (
        "hidden" => Array (
            "exclude" => 1,
            "label" => "LLL:EXT:lang/locallang_general.xml:LGL.hidden",
            "config" => Array (
                "type" => "check",
                "default" => "0"
        "starttime" => Array (
            "exclude" => 1,
            "label" => "LLL:EXT:lang/locallang_general.xml:LGL.starttime",
            "config" => Array (
                "type" => "input",
                "size" => "8",
                "max" => "20",
                "eval" => "date",
                "default" => "0",
                "checkbox" => "0"
        "endtime" => Array (
            "exclude" => 1,
            "label" => "LLL:EXT:lang/locallang_general.xml:LGL.endtime",
            "config" => Array (
                "type" => "input",
                "size" => "8",
                "max" => "20",
                "eval" => "date",
                "checkbox" => "0",
                "default" => "0",
                "range" => Array (
                    "upper" => mktime(0,0,0,12,31,2020),
                    "lower" => mktime(0,0,0,date("m")-1,date("d"),date("Y"))
        "fe_group" => Array (
            "exclude" => 1,
            "l10n_mode" => "mergeIfNotBlank",
            "label" => "LLL:EXT:lang/locallang_general.xml:LGL.fe_group",
            "config" => Array (
                "type" => "select",
                "size" => 20,
                "maxitems" => 20,
                "items" => Array (
                    Array("", 0),
                    Array("LLL:EXT:lang/locallang_general.xml:LGL.hide_at_login", -1),
                    Array("LLL:EXT:lang/locallang_general.xml:LGL.any_login", -2),
                    Array("LLL:EXT:lang/locallang_general.xml:LGL.usergroups", "--div--")
                "foreign_table" => "fe_groups",
                "foreign_table_where" => "ORDER BY title",
                "itemListStyle" => "width:350px;",
                "selectedListStyle" => "width:350px;",
        "title" => Array (
            "exclude" => 1,
            "label" => "LLL:EXT:my_extension/locallang_db.xml:tx_myextension_table.title",
            "config" => Array (
                "type" => "input",
                "size" => "50",
                "max"  => "250",
                "eval" => "required,trim",
        "content" => Array (
            "exclude" => 1,
            "label" => "LLL:EXT:my_extension/locallang_db.xml:tx_myextension_table.content",
            "config" => Array (
                "type" => "text",
                "cols" => "30",
                "rows" => "5",
                "wizards" => Array(
                    "_PADDING" => 2,
                    "RTE" => Array(
                        "notNewRecords" => 1,
                        "RTEonly" => 1,
                        "type" => "script",
                        "title" => "Full screen Rich Text Editing|Formatteret redigering i hele vinduet",
                        "icon" => "wizard_rte2.gif",
                        "script" => "wizard_rte.php",
            "defaultExtras" => "richtext[]:rte_transform[mode=ts]",
    "types" => Array (
        "0" => Array("showitem" => "--div--;LLL:EXT:my_extension/locallang_db.xml:tx_myextension_table.div1, title, content")
    "palettes" => Array (
        "1" => Array("showitem" => "starttime, endtime"),
        "2" => Array("showitem" => "fe_group")


This code has several flaws:

  • Using t3lib_extMgm, must be replaced with \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
  • Obsolete TCA options dividers2tabs and canNotCollapse are used
  • Usage of deprecated (TYPO3 CMS 7.6) / removed (TYPO3 v8) options dynamicConfigFile and feInterface
  • LLL:EXT:lang/locallang_general.xml must be replaced with LLL:EXT:lang/locallang_general.xlf
  • Obsolete configuration of the RTE wizard (changed in TYPO3 CMS 7.4)

Bring it on

The next step is not mandatory, but increases the overall performance of TYPO3: Place the TCA in Configuration/TCA/. All files in Configuration/TCA/ and Configuration/TCA/Overrides/ are processed and the final array is cached. ext_tables.php is not cached and is executed with every call.

The very first step is creating a dedicated file in typo3conf/ext/my_extension/Configuration/TCA/. The filename matches always the table name. The table name is tx_myextension_table, so the file name must be tx_myextension_table.php.

Does your extension provide a new database table and thus TCA? Place it in Configuration/TCA/. Does your extension extend or override existing TCA? Put it in Configuration/TCA/Overrides/.

For this table, we move the definition placed in ext_tables.php into our new TCA file. But behold: files in Configuration/TCA/ return(!) an array, while files in Configuration/TCA/Overrides/ manipulate $GLOBALS['TCA'] directly.

You may also put the specific TCA placed in tca.php into the matching TCA file.

$_EXTKEY is not available in any other file than ext_tables.php, ext_localconf.php and ext_emconf.php. This means, you must use the extension name as a string. In further versions of TYPO3 this variable will vanish.


After that, we simply find and replace LLL:EXT:lang/locallang_general.xml with LLL:EXT:lang/locallang_general.xlf.


The next easy task is replacing the iconfile definition: ExtensionManagementUtility::extRelPath('my_extension') is not required anymore, you may use EXT:my_extension now:

'iconfile' => 'EXT:my_extension/icon_tx_myextension_table.gif',

The exception confirms the rule...

As you may noticed, the TCA has a defintion for the column endtime using a range whose limits are defined by PHP functions. The results of those functions are cached as well, so you need to put that definition into your ext_tables.php, as that file is not cached:

$range = array(
    'upper' => mktime(0, 0, 0, 12, 31, 2020),
    'lower' => mktime(0, 0, 0, date('m') - 1, date('d'), date('Y'))
$GLOBALS['TCA']['my_extension_table']['columns']['endtime']['config']['range'] = $range;

If there is a more appropriate way to solve this, please feel free contact me ;).


Now it's time to fix the columns with a 'type' => 'select' configuration. Since the FormEngine refactoring in TYPO3 CMS 7.4, less magic is applied and you need to apply a renderType configuration, otherwise the FormEngine will die with an exception. You must now apply a renderType configuration to tell FormEngine how that field must be rendered.

For the field "fe_group" in our example above, you probably want a list from which you pick the matching values as a selection. In this case, the renderType selectMultipleSideBySide is what you want.


One thing changed with TYPO3 7 which will break your TCA: If you use wizards and only supply a script configuration, it will die as script is not supported anymore. In any case, replace it with module, this is supported since TYPO3 CMS 6.2. In case of the RTE, the configuration of the wizard changes:

'wizards' => array(
    'RTE' => array(
        'notNewRecords' => 1,
        'RTEonly' => 1,
        'type' => 'script',
        'title' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext.W.RTE',
        'icon' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_rte.gif',
        'module' => array(
            'name' => 'wizard_rte'

While we're at it, title and icon were also fixed, the latter one would trigger an entry in the deprecation log if we'd keep it untouched. But what happened to _PADDING? It's gone, forever.

Although it's not in this code, one thing is left: You were able to pass configuration, like my_field;;;richtext[]:rte_transform[mode=ts] in ['types']['0']['showitem'] - don't do this, use defaultExtras instead (link). While you're at it: if there is a flag configuration within rte_transform[], drop it, it's obsolete now.


Drop it. It's either deprecated or has been removed already, depending on the TYPO3 version you're using.

That's the end of the first part, the series will continue with plugin registration, TypoScript includes, modWizard (former wizicon). Stay tuned.

Previous Post Next Post