Attention
This manual is no longer being maintained for TYPO3 versions 11.5 and above. The majority of the content has been migrated to the Extbase or Fluid sections in "TYPO3 Explained".
Modeling the Class Hierarchy¶
In chapter 5 in "Use inheritance in class hierarchies" we have already used class hierarchies. A relational database doesn't know about some concepts of object oriented programming - also not the concept of class hierarchies. Therefore there is a mismatch between the object oriented domain model and the relational model of the database (object-relational impedance mismatch). However there are some technical options to represent the class hierarchies. Let us look for these with a simplified class hierarchy detached from our sample extension (see figure 6-14).

Figure 6-14: A simple class hierarchy¶
The classes Organization
and Person
are specializations of the class Party
(not a
jollification, but a party in not political manner). The class Organization
is again a
generalization of the sub classes Company
and ScientificInstitution
. Now assume that
in our extension data from instances of the classes Organization
, Company
,
ScientificInstitution
and Person
(the so called concrete classes) have to be stored.
The class Party
is initially used as a container for properties and behaviour that should
be available in the concrete classes without mention them also there (to avoid redundancies is
the whole purpose of a class hierarchy). For saving the data there a some options available:
For every concrete class an own database table will be created (Concrete Table Inheritance). The table contains fields for all own and inherited properties. For the class
Company
in this case a tablecompany
is created, which contains the fieldsname
,number_of_employees
andtype_of_business
. The tableperson
only contains the fieldsname
anddate_of_birth
.For all classes of a class hierarchy only one table is created (Single Table Inheritance). This table is assigned to the class which contains all sub classes which data has to be stored inside. In our case this would be the table
party
with fields for all properties of all sub classes:name
,number_of_employees
,type_of_business
,research_focus
anddate_of_birth
.For every class of the class hierarchy an own table is created in which only the properties are stored which are defined in the class (Class Table Inheritance). The table
party
in this case contains the fieldname
, the tableorganization
only the fieldnumber_of_employees
and the tablecompany
accordingly the fieldtype_of_business
:
This time Extbase and the backend of TYPO3 are supporting the first two options. For the first option Concrete Table Inheritance you have only to create the tables and configure them as described in the TCA. No additional configuration for the class hierarchy is needed.
For the second case of Single Table Inheritance beside creation of the table there is an additional configuration effort needed. Furthermore the table must have an additional field, that contains the type of the stored database tuple. The table definition schematically looks like this:
CREATE TABLE tx_myextension_domain_model_party {
uid int(11) unsigned DEFAULT '0' NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
record_type varchar(255) DEFAULT '' NOT NULL,
name varchar(255) DEFAULT '' NOT NULL,
number_of_employees int(11) unsigned DEFAULT '0' NOT NULL,
date_of_birth int(11) unsigned DEFAULT '0' NOT NULL,
type_of_business varchar(255) DEFAULT '' NOT NULL,
research_focus varchar(255) DEFAULT '' NOT NULL,
PRIMARY KEY (uid),
KEY parent (pid)
}
The name of the field that contains the type can be chosen freely. In our case it is the field
record_type
. The field name must be specified in the ctrl
section of the TCA as type
:
// …
'ctrl' => [
'title' => 'Party',
'label' => 'name',
'type' => 'record_type',
// …
],
// …
Exmaple of the previous typoscript defintions. (until TYPO3 v9)
config.tx_extbase.persistence.classes {
MyVendor\MyExtension\Domain\Model\Organization {
mapping {
tableName = tx_myextension_domain_model_party
recordType = MyVendor\MyExtension\Domain\Model\Organization
}
subclasses {
\MyVendor\MyExtension\Domain\Model\Company = MyVendor\MyExtension\Domain\Model\Company
\MyVendor\MyExtension\Domain\Model\ScientificInstitution = MyVendor\MyExtension\Domain\Model\ScientificInstitution
}
}
MyVendor\MyExtension\Domain\Model\Person {
mapping {
tableName = tx_myextension_domain_model_party
recordType = \MyVendor\MyExtension\Domain\Model\Person
}
}
MyVendor\MyExtension\Domain\Model\Company {
mapping {
tableName = tx_myextension_domain_model_party
recordType = \MyVendor\MyExtension\Domain\Model\Company
columns {
crdate.mapOnProperty = customCreationDateField
}
}
}
MyVendor\MyExtension\Domain\Model\ScientificInstitution {
mapping {
tableName = tx_myextension_domain_model_party
recordType = \MyVendor\MyExtension\Domain\Model\ScientificInstitution
}
}
}
Changed in version 10.4: With TYPO3 v10 the relationship between classes and tables has been moved into the file
Configuration/Extbase/Persistence/Classes.php
.
You have to tell Extbase for every concrete class in which table the
data of the instances are stored and with which type they should be stored.
This is done in Configuration/Extbase/Persistence/Classes.php
.
<?php
declare(strict_types = 1);
return [
\MyVendor\MyExtension\Domain\Model\Organization::class => [
'tableName' => 'tx_myextension_domain_model_party',
'recordType' => '\MyVendor\MyExtension\Domain\Model\Organization',
'subclasses' => [
'\MyVendor\MyExtension\Domain\Model\Company' => \MyVendor\MyExtension\Domain\Model\Company::class,
'\MyVendor\MyExtension\Domain\Model\ScientificInstitution' => MyVendor\MyExtension\Domain\Model\ScientificInstitution::class,
]
],
\MyVendor\MyExtension\Domain\Model\Person::class => [
'tableName' => 'tx_myextension_domain_model_party',
'recordType' => '\MyVendor\MyExtension\Domain\Model\Person',
],
\MyVendor\MyExtension\Domain\Model\Company::class => [
'tableName' => 'tx_myextension_domain_model_party',
'recordType' => '\MyVendor\MyExtension\Domain\Model\Company',
'properties' => [
'customCreationDateField' => [
'fieldName' => 'crdate',
],
],
],
\MyVendor\MyExtension\Domain\Model\ScientificInstitution::class => [
'tableName' => 'tx_myextension_domain_model_party',
'recordType' => '\MyVendor\MyExtension\Domain\Model\ScientificInstitution',
],
];
Every class is assigned with tableName = tx_myextension_domain_model_party
to this table.
In recordType
inside the table an unique identifier is expected (even the Record Type).
It is advisable to use the class name for this. For every super class additional all subclasses
have to be declared under subclasses
. In our example Party
and Organization
are
super classes, but only the class Organization
should could be instantiated. For this it is
enough to configure these class. The two subclasses Company
and ScientificInstitution
are
specified. First it looks weird that at both sides of the equation sign the same class name stands.
On the right side the really the name of the sub class must be given. On the left side only an
unique identifier inside TYPO3 is expected, so that this configuration can be extended by other
extensions without any risk if necessary. Once again it is recommend to use the class name.
You can create new objects of the classes Person
, Organization
, Company
or
ScientificInstitution
in the frontend as normal. Extbase will store them in the table
party
and put the class name in the type field. During reconstruction, when the object
is transported from the database to the memory, Extbase identifies the class on the basis of
the type field and instantiated a corresponding object.
Normally objects also should be created and edited in the backend. But the backend doesn't know the concept of classes. For this you must provide a select field in the form with this the backend user can choose the class in terms of the Record Type. This can be done with including the following configuration to your TCA:
// …
'types' => [
'0' => [
'showitem' => 'record_type, name'
],
'\MyVendor\MyExtension\Domain\Model\Organization' => [
'showitem' => 'record_type, name, numberOfEmployees'
],
'\MyVendor\MyExtension\Domain\Model\Person' => [
'showitem' => 'record_type, name, dateOfBirth'
],
'\MyVendor\MyExtension\Domain\Model\Company' => [
'showitem' => 'record_type, name, numberOfEmployees, typeOfBusiness'
],
'\MyVendor\MyExtension\Domain\Model\ScientificInstitution' => [
'showitem' => 'record_type, name, numberOfEmployees, researchFocus'
]
],
'columns' => [
// …
'record_type' => [
'label' => 'Domain Object',
'config' => [
'type' => 'select',
'items' => [
['undefined', '0'],
['Organization', '\MyVendor\MyExtension\Domain\Model\Organization'],
['Person', '\MyVendor\MyExtension\Domain\Model\Person'],
['Company', '\MyVendor\MyExtension\Domain\Model\Company'],
['ScientificInstitution', '\MyVendor\MyExtension\Domain\Model\ScientificInstitution']
],
'default' => '\MyVendor\MyExtension\Domain\Model\Person'
],
],
// …
],
// …
In the section ctrl
the type field record_type
is configured as selection list.
With this the desired domain object respectively the class name can be chosen. This impacts the
display of the form fields. In the section types
for every Record Type (in our case the class name)
the fields to be displayed are defined; when the Record Type changes the new set of form fields
are displayed after a confirmation by TYPO3.
You can access the objects via repositories as normal. In your controller the corresponding lines can look like this:
<?php
declare(strict_types = 1);
namespace MyVendor\MyExtension\Controller;
use MyVendor\MyExtension\Domain\Repository\CompanyRepository;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class CompanyController extends ActionController
{
/**
* @var CompanyRepository
*/
private $companyRepository;
/**
* Inject the company repository
*
* @param CompanyRepository $companyRepository
*/
public function injectCompanyRepository(CompanyRepository $companyRepository)
{
$this->companyRepository = $companyRepository;
}
/**
* List Action
*
* @return void
*/
public function listAction()
{
$companies = $this->companyRepository->findAll();
$this->view->assign('companies', $companies);
}
}
You can also find straightforward all concrete classes of a super class:
<?php
declare(strict_types = 1);
namespace MyVendor\MyExtension\Controller;
use MyVendor\MyExtension\Domain\Repository\OrganizationRepository;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class OrganizationController extends ActionController
{
/**
* @var OrganizationRepository
*/
private $organizationRepository;
/**
* @param OrganizationRepository $organizationRepository
*/
public function injectOrganizationRepository(OrganizationRepository $organizationRepository)
{
$this->organizationRepository = $organizationRepository;
}
public function listAction()
{
$organizations = $this->organizationRepository->findAll();
// ...
}
}
In the result set $organizationRepository
there are domain objects of the class
\MyVendor\MyExtension\Domain\Model\Organization
and all configured subclasses \MyVendor\MyExtension\Domain\Model\Company
and \MyVendor\MyExtension\Domain\Model\ScientificInstitution
are included. The query of a super class
is only possible for Single Table Inheritance this time. In the future this should also be possible
for Concrete Table Inheritance.
Note
A prominent example for the Single Table Inheritance is the table tt_content
, in which all
types of content elements are stored. Every extension can enhance the table with own fields.
Accordingly big is the amount of columns of this table. The type of the content elements is
stored in the field CType
.
With this chapter we close the work on the domain model and whose storage firstly. During the process of real development projects this process naturally is not linear. Again and again we come back to the domain model for enhance something or to optimize. In the following chapters we will dedicate to the "Flow" through the extension. In the so called Controllers we define the sequences inside the extension.