.. include:: /Includes.rst.txt .. index:: Class Hierarchy .. _modeling-the-class-hierarchy: ============================ 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 object-oriented programming concepts - 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:: /Images/Graphics/6-Persistence/figure-6-14.png 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 a political manner). The class `Organization` is again a generalization of the subclasses `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 behavior 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 are 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 table `company` is created, which contains the fields `name`, `number_of_employees` and `type_of_business`. The table `person` only contains the fields `name` and `date_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 subclasses in which data has to be stored in. In our case, this would be the table `party` with fields for all properties of all subclasses: `name`, `number_of_employees`, `type_of_business`, `research_focus` and `date_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 field `name`, the table `organization` only the field `number_of_employees` and the table `company` accordingly the field `type_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 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 the table's creation, 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: .. code-block:: sql :caption: EXT:my_extension/ext_tables.sql 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`: .. code-block:: php :caption: Configuration/TCA/tx_myextension_domain_model_party.php // … 'ctrl' => [ 'title' => 'Party', 'label' => 'name', 'type' => 'record_type', // … ], // … .. versionchanged:: 10.4 With TYPO3 v10 the relationship between classes and tables has been moved into the file :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 :file:`Configuration/Extbase/Persistence/Classes.php`. .. code-block:: php :caption: EXT:my_extension/Configuration/Extbase/Persistence/Classes.php [ '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 superclass additional all subclasses have to be declared under `subclasses`. In our example, `Party` and `Organization` are superclasses, but only the class `Organization` should be instantiated. For this, it is enough to configure these classes. 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 real name of the subclass must be given. On the left side, only a 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 recommended 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 based on the type field and instantiates 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 by including the following configuration to your TCA: .. code-block:: php :caption: Configuration/TCA/tx_myextension_domain_model_party.php // … '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 a 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: .. code-block:: php :caption: EXT:my_extension/Classes/Controller/CompanyController.php companyRepository = $companyRepository; } /** * List Action */ public function listAction(): ResponseInterface { $companies = $this->companyRepository->findAll(); $this->view->assign('companies', $companies); return $this->responseFactory->createHtmlResponse($this->view->render()); } } You can also find straightforward all concrete classes of a super class: .. code-block:: php :caption: EXT:my_extension/Classes/Controller/OrganizationController.php organizationRepository = $organizationRepository; } public function listAction(): ResponseInterface { $organizations = $this->organizationRepository->findAll(); // ... } } In the result set :php:`$organizationRepository` there are domain objects of the class `\MyVendor\MyExtension\Domain\Model\Organization` and all configured subclasses :php:`\MyVendor\MyExtension\Domain\Model\Company` and :php:`\MyVendor\MyExtension\Domain\Model\ScientificInstitution` are included. The query of a superclass 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 its own fields. Accordingly big is the number 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 to enhance something or to optimize. In the following chapters, we will dedicate ourselves to the "Flow" through the extension. In the so-called *Controllers*, we define the sequences inside the extension.