dashifen / data-transfer-object
An object that exists to transfer data.
Requires
- php: >=8.4
- dashifen/debugging: ^1.0
- dashifen/exception: ^1.2
This package is auto-updated.
Last update: 2025-03-10 18:51:55 UTC
README
A data transfer object (DTO) is an object that takes information from one place and moves it to another. Frequently, I use these to avoid having to pass arrays around because my IDE (and many others) can provide code hinting for objects but may struggle to do so for arrays.
Installation
composer require dashifen/data-transfer-object
Usage
There are two ways to use my DTO objects:
- Extending the
AbstractDTO
object, or - Extending the
DTO
object.
In either case, instantiating a DTO requires an associative array as an argument to its constructor. That array should be a map linking property names to their values. For example:
class FooBar extends DTO { protected(set) int $foo; protected(set) int $bar; } $example = new FooBar(['foo' => 1, 'bar' => 2]);
The constructor will then assign the value 1 to the foo property and 2 to bar.
Extending AbstractDTO
The AbstractDTO
object requires that you implement a toArray
method for yourself. It also throws a DTOException
from its constructor if there are unmet requirements (see below).
Extending DTO
The DTO
object is, itself, an extension of the AbstractDTO
. It does two things for you:
- Catches the
DTOException
thrown in the constructor and uses thecatcher
method of theDebuggingTrait
to handle it. - Implements the
toArray
method returning an array of any properties that are publicly available, even if asymmetrically so or virtual.
Asymmetric Visibility
In PHP 8.4, asymmetric visibility allows us to create properties that can be accessed publicly but set only within the class.
Both properties with public
and protected(set)
visibility can be set via the constructor in the AbstractDTO
object even within your extensions. If you want to use private(set)
visibility, you'll need to override it because private setters are only scoped within their own object and neither in ancestors nor children.
Required Properties
If there are properties that should be required, i.e. that must be set in your object's constructor, specify them by name as a protected array of requirements in its definition:
class FooBar extends DTO { protected array $requirements = ['foo']; protected(set) int $foo; protected(set) int $bar; }
Getters and Setters
I recommend specifying both getters and setters using property hooks:
class Example extends DTO { protected(set) string $usPhone { set { $this->usPhone = preg_replace('~\D~', '', $value); } get { return vsprintf('+1 (%s) %s-%s', [ substr($this->usPhone, 0, 3), substr($this->usPhone, 3, 3), substr($this->usPhone, 6), ]); } } }
Repositories
These are the successors to my so-called repository object. With the release of 8.4 and both property hooks and asymmetric property visibility, the work that my repository objects was doing to produce properties that could be publicly read but set only with an object (and, perhaps, only once) is unnecessary.