Software Design Principles

The following principles are considered best practices and are good to know when you develop extensions for TYPO3.

We also recommend to study common software design patterns.

DTO / Data Transfer Objects

A very common pattern in Extbase extensions is a "DTO" ("Data Transfer Object").

A DTO is an instance of a basic class that usually only has a constructor, getters and setters. It is not meant to be an extension of an Extbase AbstractEntity.

This DTO serves as pure data storage. You can use it to receive and retrieve data in a <f:form> fluid CRUD ("Create Read Update Delete") setup.

Later on, the DTO can be accessed and converted into a "proper" Extbase domain model entity: The DTO getters retrieve the data, and the Extbase domain model entity's setters receive that data:

Example of DTO and AbstractEntity used in an Extbase controller
<?php

declare(strict_types=1);

// The actual "plain" DTO, just setters and getters
class PersonDTO
{
    protected string $first_name;
    protected string $last_name;

    public function getFirstName(): string
    {
        return $this->first_name;
    }

    public function setFirstName(string $first_name): void
    {
        $this->first_name = $first_name;
    }

    public function getLastName(): string
    {
        return $this->last_name;
    }

    public function setLastName(string $last_name): void
    {
        $this->last_name = $last_name;
    }
}

// The Extbase domain model entity.
// Note that the getters and setters can easily be mapped
// to the DTO due to their same names!
class Person extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
    protected string $first_name;
    protected string $last_name;

    public function getFirstName(): string
    {
        return $this->first_name;
    }

    public function setFirstName(string $first_name): void
    {
        $this->first_name = $first_name;
    }

    public function getLastName(): string
    {
        return $this->last_name;
    }

    public function setLastName(string $last_name): void
    {
        $this->last_name = $last_name;
    }
}

// An Extbase controller utilizing DTO-to-entity transfer
class DtoController extends TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
    public function __construct(protected MyVendor\MyExtension\Domain\Repository\PersonRepository $personRepository) {}

    public function createAction(): Psr\Http\Message\ResponseInterface
    {
        // Set up a DTO to be filled with input data.
        // The Fluid template would use <f:form> and its helpers.
        $this->view->assign('personDTO', new PersonDTO());
    }

    public function saveAction(PersonDTO $personDTO): Psr\Http\Message\ResponseInterface
    {
        // Transfer all data to a proper Extbase entity.
        // Create an empty entity first:
        $person = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(Person::class);

        // Use setters/getters for propagation
        $person->setFirstName($personDTO->getFirstName());
        $person->setLastName($personDTO->getLastName());

        // Persist the extbase entity
        $this->personRepository->add($person);

        // The "old" DTO needs to further processing.
    }
}
Copied!

DTOs are helpful because:

  • They allow to decouple pure data from processed/validated data in entities.
  • They allow to structure data into distinct data models.
  • They allow to use good type hinting and using setters/getters instead of using untyped and unstructured PHP arrays for data storage.
  • They can be used to hide implementation details of your actual entities by transferring data like filter settings that internally gets applied to actual data models.

Some more reading: