Skip to content

Commit

Permalink
[TASK] Create stub for DTO explanation (TYPO3-Documentation#4823)
Browse files Browse the repository at this point in the history
* [TASK] Create stub for DTO explanation

This is a basic first step definition of a DTO

Addresses TYPO3-Documentation#4822

* [TASK] CGL
  • Loading branch information
garvinhicking authored Oct 14, 2024
1 parent 12c3ac1 commit bcf3b75
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,40 @@ extensions for TYPO3.

We also recommend to study common `software design
patterns <https://designpatternsphp.readthedocs.io/en/latest/>`__.

.. _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 `<f:form>` fluid
`CRUD <https://en.wikipedia.org/wiki/Create,_read,_update_and_delete>`__
("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:

* `<https://usetypo3.com/dtos-in-extbase/>`__
88 changes: 88 additions & 0 deletions Documentation/ExtensionArchitecture/BestPractises/_dto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?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.
}
}

0 comments on commit bcf3b75

Please sign in to comment.