Model: a bag of tea

We keep the model basic: Each tea can have a title, a description, and an optional image.

The title and description are strings, the image is stored as a relation to the model class \TYPO3\CMS\Extbase\Domain\Model\FileReference , provided by Extbase.

TCA - Table Configuration Array

Changed in version 13.0

TYPO3 creates the database scheme automatically from the TCA definition. The file ext_tables.sql can be removed on dropping TYPO3 12.4 support.

The TCA tells TYPO3 about the database model. It defines all fields containing data and all semantic fields that have a special meaning within TYPO3 (like the deleted field which is used for soft deletion).

The TCA also defines how the corresponding input fields in the backend should look.

The TCA is a nested PHP array. In this example, we need the the following keys on the first level:

ctrl
Settings for the complete table, such as a record title, a label for a single record, default sorting, and the names of some internal fields.
columns
Here we define all fields that can be used for user input in the backend.
types
We only have one type of tea record, however it is mandatory to describe at least one type. Here we define the order in which the fields are displayed in the backend.

TCA ctrl - Settings for the complete table

EXT:tea/Configuration/TCA/tx_tea_domain_model_tea.php
[
    'ctrl' => [
        'title' => 'LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_tea',
        'label' => 'title',
        'tstamp' => 'tstamp',
        'crdate' => 'crdate',
        'delete' => 'deleted',
        'default_sortby' => 'title',
        'iconfile' => 'EXT:tea/Resources/Public/Icons/Record.svg',
        'searchFields' => 'title, description',
        'enablecolumns' => [
            'fe_group' => 'fe_group',
            'disabled' => 'hidden',
            'starttime' => 'starttime',
            'endtime' => 'endtime',
        ],
        'transOrigPointerField' => 'l18n_parent',
        'transOrigDiffSourceField' => 'l18n_diffsource',
        'languageField' => 'sys_language_uid',
        'translationSource' => 'l10n_source',
    ],
]
Copied!

title

Defines the title used when we are talking about the table in the backend. It will be displayed on top of the list view of records in the backend and in backend forms.

The title of the tea table.

Strings starting with LLL: will be replaced with localized text. See chapter Extension localization. All other strings will be output as they are. This title will always be output as "Tea" without localization:

EXT:tea/Configuration/TCA/tx_tea_domain_model_tea.php
[
    'ctrl' => [
        'title' => 'Tea',
    ],
]
Copied!

label

The label is used as name for a specific tea record. The name is used in listings and in backend forms:

The label of a tea record.

tstamp, deleted, ...

These fields are used to keep timestamp and status information for each record. You can read more about them in the TCA Reference, chapter Table properties (ctrl).

TCA columns - Defining the fields

All fields that can be changed in the TYPO3 backend or used in the Extbase model have to be listed here. Otherwise they will not be recognized by TYPO3.

The title field is defined like this:

EXT:tea/Configuration/TCA/tx_tea_domain_model_tea.php
[
    'columns' => [
        'title' => [
            'label' => 'LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_tea.title',
            'config' => [
                'type' => 'input',
                'size' => 40,
                'max' => 255,
                'eval' => 'trim',
                'required' => true,
            ],
        ],
    ],
]
Copied!

The title of the field is displayed above the input field. The type is a (string) input field. The other configuration values influence display (size of the input field) and or processing on saving ( 'eval' => 'trim' removes whitespace).

You can find a complete list of available input types and their properties in the TCA Reference, chapter "Field types (config > type)".

The other text fields are defined in a similar manner.

The image field

Field type File can be used to upload files. As the image should be an image, we limit the allowed file extensions to the common-image-types. See also TCA type 'file', property 'allowed'.

EXT:tea/Configuration/TCA/tx_tea_domain_model_tea.php
<?php

return [
    // ...
    'columns' => [
        'image' => [
            'label' => 'LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_tea.image',
            'type' => 'file',
            'maxitems' => 1,
            'appearance' => [
                'collapseAll' => true,
                'useSortable' => false,
                'enabledControls' => [
                    'hide' => false,
                ],
            ],
            'allowed' => 'common-image-types',
        ],
    ],
];
Copied!

TCA types - Configure the input form

EXT:tea/Configuration/TCA/tx_tea_domain_model_tea.php
[
    'types' => [
        1 => [
            'showitem' => '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
                    title, description, image, owner,
                 --div--;LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_tea.tabs.access,
                    --palette--;;hidden,
                    --palette--;;access,',
        ],
    ],
]
Copied!

The key showitem lists all fields that should be displayed in the backend input form, in the order they should be displayed.

Result - the complete TCA

Have a look at the complete file EXT:tea/Configuration/TCA/tx_tea_domain_model_tea.php.

Now the edit form for tea records will look like this:

The complete input form for a tea record.

The list of teas in the module Web -> List looks like this:

A list of teas in the backend.

The Extbase model

It is a common practice — though not mandatory — to use PHP objects to store the data while working on it.

The model is a more abstract representation of the database schema. It provides more advanced data types, way beyond what the database itself can offer. The model can also be used to define validators for the model properties and to specify relationship types and rules (should relations be loaded lazily? Should they be deleted if this object is deleted?).

Extbase models extend the \TYPO3\CMS\Extbase\DomainObject\AbstractEntity class. The parent classes of this class already offer methods needed for persistence to database, the identifier uid etc.

Class TTN\Tea\Domain\Model\Tea
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

/**
 * This class represents a tea (flavor), e.g., "Earl Grey".
 */
class Tea extends AbstractEntity
{
    /**
     * @Extbase\Validate("StringLength", options={"maximum": 255})
     * @Extbase\Validate("NotEmpty")
     */
    protected string $title = '';

    /**
     * @Extbase\Validate("StringLength", options={"maximum": 2000})
     */
    protected string $description = '';

    /**
     * @var FileReference|null
     * @phpstan-var FileReference|LazyLoadingProxy|null
     * @Extbase\ORM\Lazy
     */
    protected $image;
}
Copied!

For all protected properties we need at least a getter with the corresponding name. If the property should be writable within Extbase, it must also have a getter. Properties that are only set in backend forms do not need a setter.

Example for the property title:

Class TTN\Tea\Domain\Model\Tea
class Tea extends AbstractEntity
{
    protected string $title = '';

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle(string $title): void
    {
        $this->title = $title;
    }
}
Copied!

The getter for the image also has to resolve the lazy loading:

Class TTN\Tea\Domain\Model\Tea
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Tea extends AbstractEntity
{
    /**
     * @var FileReference|null
     * @phpstan-var FileReference|LazyLoadingProxy|null
     * @Extbase\ORM\Lazy
     */
    protected $image;

    public function getImage(): ?FileReference
    {
        if ($this->image instanceof LazyLoadingProxy) {
            /** @var FileReference $image */
            $image = $this->image->_loadRealInstance();
            $this->image = $image;
        }

        return $this->image;
    }

    public function setImage(FileReference $image): void
    {
        $this->image = $image;
    }
}
Copied!

See the complete class on Github: Tea.