Skip to main content

Data Transfer Object V3 Modernizes DTOs With PHP 8 Features

Spatie's Data Transfer Object (DTO) package makes constructing objects from arrays a breeze, giving you confidence in the data contained therein. I've been a fan of this package since learning about the initial V1 release, and I hope you'll consider this package for passing around data in your application.

The DTO package just released V3 with all of the PHP 8 goodies we've only dreamed of up to this point. For those just starting to use or consider PHP 8 in your projects, the source code of the V3 DTO package is an excellent resource with real-world examples of helpful PHP 8 features.

I want to congratulate the principal author Brent Roose and Spatie for moving forward with this excellent package with the features in the V3 release.

Here are some of the main features taken from the readme available in V3:

Named arguments
Value Casts - convert properties typed as a DTO from array data to the DTO instance automatically
Custom Casts - you can build your custom caster classes
Strict DTOs
Helper functions
Runtime type checks are gone in favor of PHP 8's type system
If you want all the details of the above features, check out the project's readme.

While the DTO package readme delves into the more complex use-cases this package supports, here's a simple example back from V1 of what you might expect from this package for those not familiar with the DTO package:

class PostData extends DataTransferObject
{
    /** @var string */
    public $title;
    
    /** @var string */
    public $body;
    
    /** @var int */
    public $author_id;
}

$postData = new PostData([
    'title' => '…',
    'body' => '…',
    'author_id' => '…',
]);

$postData->title;
$postData->body;
$postData->author_id;
As you can see, we can pass array data to the constructor, which (at the time of V1) checks types at runtime and constructs a PostData instance or fails in an exception if the data is not valid.

With the release of typed properties in PHP 7.4 and named arguments and union types in PHP 8, Spatie's V3 of the DTO package leverages many new language features. Taking the simple example above, here's what it might look like in a PHP 8 version:

use Spatie\DataTransferObject\DataTransferObject;

class PostData extends DataTransferObject
{
    public string $title;
    public string $body;
    public int $author_id;
}

// Named arguments FTW
$post = new PostData(
    title: 'Hello World',
    body: 'This is a test post.',
    author_id: 1
);

echo $post->title, "\n";
echo $post->body, "\n";
echo $post->author_id, "\n";
The above example is simple but illustrates well how this package's basics have evolved since V1, which supports PHP ^7.0. Note: given the above example, you might not even need to leverage the DTO package as PHP 8 takes care of the typing concerns and allows named arguments making a plain old PHP object (POPO) just as practical for this simple example.

If you haven't tried Spatie's DTO package, I hope even this simple example illustrates how you can know more about the data you transfer between objects. Imagine the above as an array of data:

$post = [
    'title' => 'Hello World',
    'body' => 'This is a test post.',
    'author_id' => 1,
];

$someObject->doStuff($post);
Let's say that your doStuff implementation looks like the following to keep the example simple:

function doStuff(array $post)
{
    $title = ucwords($post['title']);
    $author = findAuthorById($post['author_id']);

    echo $title, "\n";
    echo htmlspecialchars($post['body']);
    echo $author['name'], "\n";  
}
As a consumer of doStuff(), you have no idea the shape of the required data without referencing the implementation of doStuff(). That means when you need to call doStuff(), you have to look at the function to know what array data to pass.

The maintainer of a naive doStuff() implementation assumes that the consumer is sending required data. Sending malformed or missing data results in undefined key errors that might not crop up until after you've shipped a feature. Or, if you're paranoid, you might need to check everything before you use it:

function doStuff(array $post)
{
    if (empty($post['title'])) {
        throw new \Exception('Missing title');
    }

    if (empty($post['body'])) {
        throw new \Exception('Missing body');
    }

    if (empty($post['author_id'])) {
        throw new \Exception('Missing author_id key');
    }
    
    $title = ucwords($post['title']);
    $author = findAuthorById($post['author_id']);

    echo $title, "\n";
    echo htmlspecialchars($post['body']);
    echo $author['name'], "\n";  
}
Instead, you could guarantee the shape of data with a POPO or DTO (back in V1). It's just that in DTO V2 and V3, some things are now handled natively through PHP 8's language features instead of runtime checks:

function doStuff(PostData $post)
{
    $author = findAuthorById($post->author_id);
    
    echo $post->title, "\n";
    echo htmlspecialchars($post->body);
    echo $author->name, "\n";  
}
IDEs understand PostData, making it easy to both use and construct new instances. In this naive example, we know what data to expect, and the DTO package ensures this data is structured as expected when the object gets constructed.

While structured, typed data might seem like a trivial boilerplate to the veteran Java developer, PHP developers are more prone to seeing associative array data passed back and forth in an application. PHP 8 and this DTO package go a long way in providing more assurances of the data passed around in your PHP applications, which I believe makes you more productive and your code more confident.

Popular posts from this blog

Laravel8 in Serializes Models trait | laravelnote

This article was originally posted, with additional formatting, on my personal blog at laravel serializes model Background  When dispatching an object onto the queue, behind the scenes Laravel is recursively serializing the object and all of its properties into a string representation that is then written to the queue. There it awaits a queue worker to retrieve it from the queue and unserialize it back into a PHP object (Phew!). Problem When complicated objects are serialized, their string representations can be atrociously long, taking up unnecessary resources both on the queue and application servers. Solution Because of this, Laravel offers a trait called SerializesModels which, when added to an object, finds any properties of type Model or Eloquent\Collection during serialization and replaces them with a plain-old-PHP-object (POPO) known as a ModelIdentifier. These identifier objects represent the original properties Model type and ID, or IDs in the case of an Eloquent\Collecti...

Octane Support in Laravel forge 2021 5 may

 Back in March, Taylor announced the new Laravel Octane project at Laracon Online. Since then, we've been busy working on bug fixes and enhancements to Octane, as well as adding support to Laravel Forge. Today we're pleased to announce that Octane support is available in Forge. We've written the guide below to help you get started. Please keep in mind that Octane is still in beta and should not be used in production. Prerequisites Your project must require "laravel/octane": "^0.3.2" or above. Your server must have PHP 8.0 installed. You should then follow the Octane installation instructions listed in the Octane repository. Creating an Octane Site Octane can be enabled by selecting the Laravel Octane (Beta) project type option and PHP 8.0 as the PHP version that should be used to serve your site: Laravel Octane Project Type Laravel Octane Project Type Once the project type has been selected, Forge will ask for the port that Octane should listen on. Unlik...

Create Your Next Project's Readme in Laravel8 | laravelnote

  The readme.so editor gives you visual cues, starter section templates and includes many standard readme sections you're likely to use. It also has a nice preview to help guide you along the way. Never forget a section for your readme again! Select sections to add to your readme, edit the contents, and drag to rearrange. See a live-updating rendered preview of your markdown, then download your README.md file! Here's an example of starting an API section, which provides helpful formatting. You might need to specify API params: The editor includes both light and dark editor support, and you can download or copy/paste the raw markdown of your readme into version control once you're done. You can learn more about this project and start using it on  readme.so . Also, be sure to check out  readme.so on Product Hunt  and upvote it if you find it useful!