AthenaeumAthenaeum
Packages
  • next
  • current
  • v9.x
  • v8.x
  • v7.x
  • v6.x
  • v5.x
  • v4.x
  • v3.x
  • v2.x
  • v1.x
Changelog
GitHub
Packages
  • next
  • current
  • v9.x
  • v8.x
  • v7.x
  • v6.x
  • v5.x
  • v4.x
  • v3.x
  • v2.x
  • v1.x
Changelog
GitHub
  • Version 6.x

    • Release Notes
    • Upgrade Guide
    • New to this...
    • Contribution Guide
    • Security Policy
    • Code of Conduct
    • Origin
  • ACL

    • Introduction
    • How to install
    • Setup
    • Permissions
    • Roles
    • Users
    • Cached Permissions
  • Audit

    • Audit
    • How to install
    • Setup
    • Recording
    • Events
  • Circuits

    • Circuits
    • How to install
    • Setup
    • Usage
    • Events
  • Collections

    • Collections
    • How to install
    • Summation

      • Summation Collection
      • Items Processor
  • Config

    • Configuration Loader
    • How to install
    • Setup
    • Load Configuration Files
    • Custom File Parsers
  • Console

    • Command and Schedule Registration
    • How to install
    • Setup
    • Commands
    • Schedules
  • Container

    • IoC Service Container
    • How to install
    • Container
    • List Resolver
  • Core

    • Athenaeum Core Application
    • How to install
    • Setup
    • Usage

      • Configuration
      • Service Providers
      • Service Container
      • Events
      • Caching
      • Logging
      • Console
      • Task Scheduling
      • Exception Handling
      • Extending Core Application
      • Testing
  • Database

    • Introduction
    • How to install
    • Models

      • Instantiatable
      • Sluggable
    • Query

      • Criteria (Query Filter)
  • Dto

    • Data Transfer Object (DTO)
    • How to install
    • Create Interface
    • Implement DTO
    • How to use
    • Populate
    • Export
    • Json
    • Serialization
    • Nested DTOs
    • Array DTO
  • ETags

    • ETags
    • How to install
    • Setup
    • Usage
    • Generators

      • Default Generator
      • Custom Generator
    • Eloquent Models
    • Macros
  • Events

    • Register Listeners and Subscribers
    • How to install
    • Setup
    • Listeners
    • Subscribers
  • Filters

    • Search Filter Utilities
    • Prerequisites
    • How to install
    • Setup
    • Processor
    • Filters Builder
    • Predefined Resources

      • Search Processor
      • Sorting Processor
      • Constraints Processor
      • Matching Processor
    • Tip: Create a base builder
  • Flysystem

    • Introduction
    • Database Adapter

      • Introduction
      • How to install
      • Setup
      • Data Deduplication
      • MIME-Type Detection
  • Http

    • Api

      • Http API
      • How to install
      • Setup
      • Resources

        • Introduction
        • Timestamps
        • Self-Link
        • Relations
        • Registrar
      • Middleware

        • Introduction
        • Request Must Be Json
        • Capture Fields To Select
    • Clients

      • Http Clients
      • How to install
      • Setup
      • Basic Usage
      • Available Methods

        • Fluent Api
        • Protocol Version
        • Base Uri
        • Http Method and Uri
        • Headers
        • Accept & Content-Type
        • Authentication
        • Http Query
        • Payload Format
        • Payload
        • Attachments
        • Cookies
        • Response Expectations
        • Middleware
        • Conditions
        • Criteria
        • Redirects
        • Timeout
        • Debugging
        • Logging
        • Driver Options
        • Driver
      • Http Query Builder

        • Introduction
        • Select
        • Where
        • Dates
        • Include
        • Pagination
        • Sorting
        • Raw Expressions
        • Custom Grammar
    • Cookies

      • Http Cookies
      • How to install
      • Usage
    • Messages

      • Http Messages
      • How to install
      • Serializers
  • Maintenance

    • Modes

      • Maintenance Modes
      • How to install
      • Setup
      • Basic Usage
      • Available Drivers
  • Mime Types

    • MIME-Types
    • How to install
    • Setup
    • Usage
    • Drivers

      • Available Drivers
      • File Info
  • Properties

    • Properties Overload
    • How to install
    • Usage
    • Naming Convention
    • Properties Visibility
  • Redmine

    • Redmine Api Client
    • How to install
    • Setup
    • General Usage

      • Supported Operations
      • Fetch list of resources
      • Find
      • Fetch
      • Create new record
      • Update existing record
      • Delete existing record
      • Relations
    • Available Resources

      • Predefined Resources
      • Attachments
      • Enumerations
      • Issue Relations
      • Users
      • User Groups
      • Roles
      • Project Memberships
      • Versions (Milestones)
      • Issue Categories
      • Trackers
  • Service

    • Service Registrar
    • How to install
    • How to use
  • Streams

    • Streams
    • How to install
    • Setup
    • How to use

      • Introduction
      • Open and Close
      • Raw Resource
      • Seeking
      • Reading
      • Writing
      • Size
      • Truncate
      • Flush
      • Hash
      • MIME-Type
      • Output
      • Locking
      • Transactions
      • Meta
      • Misc
  • Support

    • Introduction
    • How to install
    • Laravel Aware-of Helpers

      • How to use
      • Enforce Via Interface
      • Custom Default
      • Pros and Cons
      • Available Helpers
    • Aware-of Properties

      • Generator
      • Available Aware-of Helpers
    • Live Templates
  • Testing

    • Introduction
    • How to install
    • Test Cases
    • Testing Aware-of Helpers
  • Utils

    • Introduction
    • How to install
    • Array
    • Duration
    • Json
    • Math
    • Memory
    • Method Helper
    • Invoker
    • Populatable
    • String
    • Version
  • Validation

    • Introduction
    • How to install
    • Setup
    • Rules

      • Alpha-Dash-Dot
      • Semantic Version
You are viewing documentation for an outdated version. It is no longer supported!

Nested DTOs

Imagine that your Person DTO accepts more complex properties, e.g. an address DTO. Normally, you would manually create that address DTO first, in order to populate your main DTO. However, if you use the Dto abstraction with Laravel's Service Container, populating nested DTOs will be automatically handled for you.

Prerequisite

If you are using the Dto component within a typical Laravel application, then you do not have to do anything. A Service Container should already be available.

If you are using this Dto package outside a Laravel application, then you must ensure that a Service Container has been initialised. Consider using this package's Service Container (a slightly adapted version of Laravel's Service Container).

  • Prerequisite
  • Example
    • Address DTO
    • Person DTO
    • Resolving Nested Dependencies
  • Union Types
    • Scalar types
    • Array types
    • Nested DTOs
    • Caveats

Example

The following example shows two DTOs; Address and Person.

Address DTO

class Address extends Dto
{
    protected ?string $street = '';

    public function setStreet(?string $street)
    {
        $this->street = $street;
    }

    public function getStreet() : ?string
    {
        return $this->street;
    }
}

Person DTO

class Person extends Dto implements PersonInterface
{
    protected ?string $name = '';
    
    protected ?int $age = 0;
 
    protected ?Address $address = null;
 
    // ... getters and setters for name and age not shown ... //

     public function setAddress(?Address $address)
     {
         $this->address = $address;
     }
     
     public function getAddress() : ?Address
     {
         return $this->address;
     }
}

Resolving Nested Dependencies

When populating your DTO, just pass in the data as your normally do. Eventual nested dependencies will automatically be attempted resolved and populated. Consider the following example:

$data = [
    'name' => 'Arial Jackson',
    'age' => 42,
    
    // Notice that we are NOT passing an instance of Address, but an array instead!
    'address' => [
        'street' => 'Somewhere str. 44'
    ]
];

$person = new Person($data);                                    
$address = $person->getAddress(); // Address DTO instance

In the above example, the Address DTO is automatically resolved and populated by the Service Container.

Note

If unable to resolve a nested dependency, the Service Container will fail with a \Psr\Container\ContainerExceptionInterface.

Union Types

If you define properties that accept union types, then the Dto attempt to populate the value accordingly.

Scalar types

When your property accepts a few scalar types, the Dto will ensure that it's data type is cast accordingly.

Example

class Person extends Dto
{
    protected string|int|null $id = null;
    
    public function setId(string|int|null $id)
    {
        $this->id = $id;
    }
    
    public function getId(): string|int|null
    {
        return $this->id;
    }
}
$person->populate([ 'id' => 'allan-james-jr']);
echo gettype($person->id); // string

$person->populate([ 'id' => 42]);
echo gettype($person->id)); // integer  

Array types

The same is true when you accept an array.

Example

class Person extends Dto
{
    protected string|array|null $name = null;
    
    public function setName(string|array|null $name)
    {
        $this->name = $name;
    }
    
    public function getName(): string|array|null
    {
        return $this->name;
    }
}
$person->populate([ 'name' =>  'Thomas Smith']);
echo gettype($person->name); // string

$person->populate([ 'name' => [ 'Thomas', 'Smith', 'Jr' ]]);
echo gettype($person->name); // array  

Nested DTOs

You may also use define properties that accept multiple nested DTOs. When populated with an array, the Dto will attempt to find the most suitable match. Consider the following example, where the property reference accepts two types of populatable DTOs.

Example

(The following examples assume that the order of the accepted types for the setter methods is in the exact same order, as declared for the class properties.)

class Person extends Dto
{
    protected string|null $name = null;
    
    // ... getters / setters not shown
}

class Organisation extends Dto
{
    protected string|null $name = null;
    
    protected string|null $slogan = null;
    
    // ... getters / setters not shown
}

class Record extends Dto
{
    protected string|Person|Organisation|null $reference = null;
    
    // ... getters / setters not shown
}
// Reference is a string...
$record->populate([
    'reference' => 'https:://google.com'
]);
echo gettype($record->reference); // string

// Reference becomes a Person...
$record->populate([
    'reference' => [ 'name' => 'Jane Jensen' ]
]);
echo ($record->reference instanceof Person); // true

// Reference becomes an Organisation...
$record->populate([
    'reference' => [ 'name' => 'Acme', 'slogan' => 'Building stuff...' ]
]);
echo ($record->reference instanceof Organisation); // true

Caveats

When populating nested DTOs with arrays, then the Dto abstraction will attempt to find the most suitable match. This means that if you accept two or more DTOs that share property names, e.g. the $name property as shown in previous examples, then the DTO will choose the first match.

For instance, if you expect the $reference to be an Organisation, yet you only provide a name, then the first nested DTO that accepts a name property will be chosen In this example, a Person instance is created and populated, instead of an Organisation.

$record->populate([
    'reference' => [ 'name' => 'Acme' ]
]);
echo ($record->reference instanceof Organisation); // false

The reason for this behaviour is due to the order in which the union types are declared (see Record class declaration in previous example). To continue the example, when you provide a property that only exists in Organisation, then the Dto will be able to match it accordingly.

$record->populate([
    'reference' => [ 'slogan' => 'Building stuff...' ]
]);
echo ($record->reference instanceof Organisation); // true

The last caveat to be mindful of, is when you choose to declare a property that accepts both an array and a DTO. If the array type is stated before your desired nested DTO, then the nested DTO will never be matched.

class Record extends Dto
{
    protected array|Organisation|null $reference = null;
    
    // ... getters / setters not shown
}

$record->populate([
    'reference' => [ 'slogan' => 'Building stuff...' ]
]);
echo ($record->reference instanceof Organisation); // false
Edit page
Last Updated: 16/02/2023, 09:10
Contributors: Alin Eugen Deac, alin
Prev
Serialization
Next
Array DTO