From bcf3b7538a03b9a944f41ebc7e75418a4ca7fa55 Mon Sep 17 00:00:00 2001 From: Garvin Hicking Date: Mon, 14 Oct 2024 10:43:17 +0200 Subject: [PATCH] [TASK] Create stub for DTO explanation (#4823) * [TASK] Create stub for DTO explanation This is a basic first step definition of a DTO Addresses #4822 * [TASK] CGL --- .../SoftwareDesignPrinciples.rst | 37 ++++++++ .../BestPractises/_dto.php | 88 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 Documentation/ExtensionArchitecture/BestPractises/_dto.php diff --git a/Documentation/ExtensionArchitecture/BestPractises/SoftwareDesignPrinciples.rst b/Documentation/ExtensionArchitecture/BestPractises/SoftwareDesignPrinciples.rst index b79731e97e..654aff5ff7 100644 --- a/Documentation/ExtensionArchitecture/BestPractises/SoftwareDesignPrinciples.rst +++ b/Documentation/ExtensionArchitecture/BestPractises/SoftwareDesignPrinciples.rst @@ -19,3 +19,40 @@ extensions for TYPO3. We also recommend to study common `software design patterns `__. + +.. _concept-dto: + +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 `` 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: + +.. literalinclude:: _dto.php + :language: php + :caption: Example of DTO and AbstractEntity used in an Extbase controller + +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: + +* ``__ diff --git a/Documentation/ExtensionArchitecture/BestPractises/_dto.php b/Documentation/ExtensionArchitecture/BestPractises/_dto.php new file mode 100644 index 0000000000..9b484c4ccf --- /dev/null +++ b/Documentation/ExtensionArchitecture/BestPractises/_dto.php @@ -0,0 +1,88 @@ +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 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. + } +}