Creating DTOs
Creating data transfer object classes and adding properties to them in a proper way so they can be mapped.
Mapped data types
Apart from being capable of passing native typed PHP properties it can also cast to a tiny range of content that the framework as well as PHP provides:
Delimited lists (
1,3,7
orhello-world,foo,bar
) can be converted to arrays or collections with typed child properties like models, dates, etc.Models binding from simple binding to morph binding using custom attributes for the binding and loading relationships.
Enums backed values can be converted to native PHP enums.
Dates in string (
2023-09-07 06:35:53
) can be converted toCarbon
instances.Authenticated user using
WithDefaultValue
PHP attribute with the Laravel's interfaceIlluminate\Contracts\Auth\Authenticatable
.
Models binding
When typing a variable with an Eloquent model class it will be mapped to this model querying the database when the instance is not passed through to the DTO.
So sending a tag
to this to be able to map (do a model binding) you should send the following:
This will query the tag with the id = 1.
Customise binding attribute
Now in case we have a Post
entity with an unique slug
column that we can use to simplify its binding:
This way you can send a post by using the slug:
Binding with relationships loaded
Ever dreamed about loading relationships from route bindings using DTOs directly in your controllers? Now is the time to show you an unique feature from this package:
Morph binding
In an example case when you have a relationship on your tags called taggable (a morph relationship) which can attach posts and films models, this is the way to deal with these on DTOs:
Now we can send something like this from the frontend or API:
The following feature is available since v3.5 so make sure you use the latest version of this package.
In case you want to mix taggable types you can do so changing the property type of taggableType
from string to array like so:
Having the one above we can do plenty combinations from the frontend or API:
In case we send more IDs than types the last type will be taken for all the IDs that are left alone.
Customise binding attribute
In an example when you have slugs only in posts but not films entities you can add the following to determine which morph type will have its binding customised:
Binding with relationships loaded
You may need to add model classes as array keys so DTO mapping will know which relationship to load on each morph type:
This way we are binding a taggable
entity that when is a post will be using slug on this property, while films will use their defaults (id
).
When loading a post will be getting its author
and author's role
, if otherwise is a film it will only load its reviews.
Mapping collections
We determine as collections arrays and Laravel's collections because of some particular mapping process we do to them, lets imagine we send this to our backend:
Using the following DTO:
We should get a array from this delimited list, now let's say we wanted to have integers, we could just use a docblock to help us.
Now we've an array of integers and so our IDE can also help us when using this property. But we're not limited to only native types, we can also use models! Sending a comma-delimited list of IDs and typing this properly.
Same will go for Laravel collections, just typing it properly like so:
The example at the top will bind tag IDs or an array of IDs or tags model instances to a collection of tags instances.
Mapping with default values
In case you want some default data mapped whenever you use fromArray
you can use withDefaults
like:
Default value using attribute
You can use PHP attributes instead, which simplifies it even more as you only need to send the raw value without it being mapped:
Also this attribute has special usages like the ones following below.
Default value as authenticated user
This was created after using Authenticatable
interface as a property type because this interface is not completed with all the types that a User
model has (or any model used in the authentication of your Laravel application).
If the right contract is sent it will grab the authenticated user:
Last updated