Software Design Principles
The following principles are considered best practices and are good to know when you develop extensions for TYPO3.
- Object-Oriented Programming (OOP) organizes software design around data, or objects, rather than the functions and logic of procedural programming.
- Domain-Driven Design (DDD) is a collection of principles and patterns that help developers design elegant code around clearly defined conceptual models.
- Model-View-Controller (MVC) is a programming paradigm that leads to clear isolation of data, presentation layer, and business logic.
- Test-Driven Development (TDD) is a basic technique for generating code that is stable, resilient to errors, and legible — and therefore maintainable.
- Dependency Injection is a software design pattern that adds flexibility by removing the need for hard-coded dependencies.
- Separation of Concerns and Single-responsibility principle (SRP) are computer programming principles that help developers create modular, coherent, and maintainable software.
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 Abstract
.
This DTO serves as pure data storage. You can use it to receive and retrieve
data in a <f:
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:
<?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.
}
}
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: