PHPackages                             neuron-php/dto - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. neuron-php/dto

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

neuron-php/dto
==============

Easy, dynamic DTO creation, validation and mapping.

0.0.16(4mo ago)0986—0%2MITPHPPHP ^8.4CI passing

Since Jan 23Pushed 4mo agoCompare

[ Source](https://github.com/Neuron-PHP/dto)[ Packagist](https://packagist.org/packages/neuron-php/dto)[ RSS](/packages/neuron-php-dto/feed)WikiDiscussions develop Synced 1mo ago

READMEChangelogDependencies (6)Versions (18)Used By (2)

[![CI](https://github.com/Neuron-PHP/dto/actions/workflows/ci.yml/badge.svg)](https://github.com/Neuron-PHP/dto/actions)[![codecov](https://camo.githubusercontent.com/7259c3f7cfc7b5047cfaa302f62003ea4b3567c0c95f21285c5681223649a708/68747470733a2f2f636f6465636f762e696f2f67682f4e6575726f6e2d5048502f64746f2f6272616e63682f646576656c6f702f67726170682f62616467652e737667)](https://codecov.io/gh/Neuron-PHP/dto)

Neuron-PHP DTO
==============

[](#neuron-php-dto)

A powerful Data Transfer Object (DTO) library for PHP 8.4+ that provides dynamic DTO creation, comprehensive validation, and flexible data mapping capabilities with support for nested structures and YAML configuration.

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Core Features](#core-features)
- [DTO Configuration](#dto-configuration)
- [Creating DTOs](#creating-dtos)
- [Validation](#validation)
- [Data Mapping](#data-mapping)
- [Property Types](#property-types)
- [Collections](#collections)
- [Advanced Usage](#advanced-usage)
- [Testing](#testing)
- [Best Practices](#best-practices)
- [More Information](#more-information)

Installation
------------

[](#installation)

### Requirements

[](#requirements)

- PHP 8.4 or higher
- Composer
- symfony/yaml (^6.4)
- neuron-php/validation (^0.7.0)

### Install via Composer

[](#install-via-composer)

```
composer require neuron-php/dto
```

Quick Start
-----------

[](#quick-start)

### 1. Define Your DTO Structure

[](#1-define-your-dto-structure)

Create a YAML configuration file (`user.yaml`):

```
dto:
  username:
    type: string
    required: true
    length:
      min: 3
      max: 20
  email:
    type: email
    required: true
  age:
    type: integer
    range:
      min: 18
      max: 120
```

### 2. Create and Use the DTO

[](#2-create-and-use-the-dto)

```
use Neuron\Dto\Factory;

// Create DTO from configuration
$factory = new Factory('user.yaml');
$dto = $factory->create();

// Set values
$dto->username = 'johndoe';
$dto->email = 'john@example.com';
$dto->age = 25;

// Validate
if (!$dto->validate()) {
    $errors = $dto->getErrors();
    // Handle validation errors
}

// Export as JSON
echo $dto->getAsJson();
```

Core Features
-------------

[](#core-features)

- **Dynamic DTO Creation**: Generate DTOs from YAML configuration files
- **Comprehensive Validation**: Built-in validators for 20+ data types
- **Nested Structures**: Support for complex, hierarchical data models
- **DTO Composition**: Reuse DTOs by referencing existing DTO definitions
- **Data Mapping**: Transform external data structures to DTOs
- **Type Safety**: Strict type checking and validation
- **Collections**: Handle arrays of objects with validation
- **JSON Export**: Easy serialization to JSON format
- **Custom Validators**: Extend with custom validation logic

DTO Configuration
-----------------

[](#dto-configuration)

### Basic Structure

[](#basic-structure)

DTOs are configured using YAML files with property definitions:

```
dto:
  propertyName:
    type: string|integer|boolean|array|object|etc
    required: true|false
    # Additional validation rules
```

### Complete Example

[](#complete-example)

```
dto:
  # Simple string property
  firstName:
    type: string
    required: true
    length:
      min: 2
      max: 50

  # Email with validation
  email:
    type: email
    required: true

  # Integer with range
  age:
    type: integer
    range:
      min: 0
      max: 150

  # Date with pattern
  birthDate:
    type: date
    pattern: '/^\d{4}-\d{2}-\d{2}$/'  # YYYY-MM-DD

  # Nested object
  address:
    type: object
    required: true
    properties:
      street:
        type: string
        required: true
        length:
          min: 5
          max: 100
      city:
        type: string
        required: true
      state:
        type: string
        length:
          min: 2
          max: 2
      zipCode:
        type: string
        pattern: '/^\d{5}(-\d{4})?$/'  # US ZIP code

  # Array of objects
  phoneNumbers:
    type: array
    items:
      type: object
      properties:
        type:
          type: string
          enum: ['home', 'work', 'mobile']
          required: true
        number:
          type: string
          pattern: '/^\+?[\d\s\-\(\)]+$/'
          required: true

  # Array of primitives
  tags:
    type: array
    items:
      type: string
      length:
        min: 1
        max: 20
```

Creating DTOs
-------------

[](#creating-dtos)

### From YAML Configuration

[](#from-yaml-configuration)

```
use Neuron\Dto\Factory;

// Load from file
$factory = new Factory('path/to/neuron.yaml');
$dto = $factory->create();

// Set properties
$dto->firstName = 'John';
$dto->email = 'john@example.com';
$dto->age = 30;
```

### Programmatic Creation

[](#programmatic-creation)

```
use Neuron\Dto\Dto;
use Neuron\Dto\Property;

$dto = new Dto();

// Create string property
$username = new Property();
$username->setName('username');
$username->setType('string');
$username->setRequired(true);
$username->addLengthValidator(3, 20);

$dto->addProperty($username);

// Create email property
$email = new Property();
$email->setName('email');
$email->setType('email');
$email->setRequired(true);

$dto->addProperty($email);
```

### Nested Objects

[](#nested-objects)

```
// Setting nested properties
$dto->address->street = '123 Main St';
$dto->address->city = 'New York';
$dto->address->state = 'NY';
$dto->address->zipCode = '10001';

// Accessing nested properties
$street = $dto->address->street;
$city = $dto->address->city;
```

### DTO Composition (Reusable DTOs)

[](#dto-composition-reusable-dtos)

You can create reusable DTO definitions and reference them in other DTOs, making it easy to share common structures like timestamps, addresses, or user records across multiple DTOs.

#### Creating Reusable DTOs

[](#creating-reusable-dtos)

First, create standalone DTO definition files:

**common/timestamps.yaml**

```
dto:
  createdAt:
    type: date_time
    required: true
  updatedAt:
    type: date_time
    required: false
```

**common/address.yaml**

```
dto:
  street:
    type: string
    required: true
    length:
      min: 3
      max: 100
  city:
    type: string
    required: true
  state:
    type: string
    required: true
    length:
      min: 2
      max: 2
  zipCode:
    type: string
    required: true
    pattern: '/^\d{5}(-\d{4})?$/'
```

**common/user.yaml**

```
dto:
  id:
    type: uuid
    required: true
  username:
    type: string
    required: true
    length:
      min: 3
      max: 20
  email:
    type: email
    required: true
  firstName:
    type: string
    required: true
  lastName:
    type: string
    required: true
```

#### Using Referenced DTOs

[](#using-referenced-dtos)

Reference these DTOs in your main DTO definition using `type: dto` with a `ref` parameter:

```
dto:
  id:
    type: uuid
    required: true

  title:
    type: string
    required: true

  # Reference to reusable timestamps DTO
  timestamps:
    type: dto
    ref: 'common/timestamps.yaml'
    required: true

  # Reference to reusable user DTO
  author:
    type: dto
    ref: 'common/user.yaml'
    required: true

  # Reference to reusable address DTO
  shippingAddress:
    type: dto
    ref: 'common/address.yaml'
    required: false
```

#### Working with Composed DTOs

[](#working-with-composed-dtos)

```
use Neuron\Dto\Factory;

// Create DTO with referenced DTOs
$factory = new Factory('article.yaml');
$dto = $factory->create();

// Set values on the main DTO
$dto->id = '550e8400-e29b-41d4-a716-446655440000';
$dto->title = 'My Article';

// Set values on referenced DTOs
$dto->timestamps->createdAt = '2024-01-01 10:00:00';
$dto->timestamps->updatedAt = '2024-01-02 12:00:00';

$dto->author->id = '550e8400-e29b-41d4-a716-446655440001';
$dto->author->username = 'johndoe';
$dto->author->email = 'john@example.com';
$dto->author->firstName = 'John';
$dto->author->lastName = 'Doe';

$dto->shippingAddress->street = '123 Main St';
$dto->shippingAddress->city = 'New York';
$dto->shippingAddress->state = 'NY';
$dto->shippingAddress->zipCode = '10001';

// Validate entire structure including referenced DTOs
$dto->validate();

// Export to JSON
echo $dto->getAsJson();
```

#### Benefits of DTO Composition

[](#benefits-of-dto-composition)

- **Reusability**: Define common structures once, use them everywhere
- **Consistency**: Ensure the same validation rules across all uses
- **Maintainability**: Update the definition in one place
- **Performance**: Referenced DTOs are cached automatically
- **Type Safety**: Full validation support for nested structures

#### Path Resolution

[](#path-resolution)

Referenced paths are resolved relative to the parent DTO file:

```
# If this file is at: project/dtos/article.yaml
# And you reference: 'common/timestamps.yaml'
# The system will look for: project/dtos/common/timestamps.yaml

# You can also use absolute paths:
timestamps:
  type: dto
  ref: '/absolute/path/to/timestamps.yaml'
```

Validation
----------

[](#validation)

### Built-in Validators

[](#built-in-validators)

The DTO component includes comprehensive validation for each property type:

```
// Validate entire DTO
if( !$dto->validate() )
{
    $errors = $dto->getErrors();
    foreach( $errors as $property => $propertyErrors )
    {
        echo "Property '$property' has errors:\n";
        foreach( $propertyErrors as $error )
        {
            echo "  - $error\n";
        }
    }
}

// Validate specific property
$usernameProperty = $dto->getProperty('username');
if (!$usernameProperty->validate()) {
    $errors = $usernameProperty->getErrors();
}
```

### Validation Rules

[](#validation-rules)

#### Length Validation

[](#length-validation)

```
username:
  type: string
  length:
    min: 3
    max: 20
```

#### Range Validation

[](#range-validation)

```
age:
  type: integer
  range:
    min: 18
    max: 65
```

#### Pattern Validation

[](#pattern-validation)

```
phoneNumber:
  type: string
  pattern: '/^\+?[1-9]\d{1,14}$/'  # E.164 format
```

#### Enum Validation

[](#enum-validation)

```
status:
  type: string
  enum: ['active', 'inactive', 'pending']
```

#### Custom Validation

[](#custom-validation)

```
use Neuron\Validation\IValidator;

class CustomValidator implements IValidator
{
    public function validate($value): bool
    {
        // Custom validation logic
        return $value !== 'forbidden';
    }

    public function getError(): string
    {
        return 'Value cannot be "forbidden"';
    }
}

// Add to property
$property->addValidator(new CustomValidator());
```

Data Mapping
------------

[](#data-mapping)

### Mapper Configuration

[](#mapper-configuration)

Create a mapping configuration (`mapping.yaml`):

```
map:
  # Simple mapping
  external.username: dto.username
  external.user_email: dto.email

  # Nested mapping
  external.user.profile.age: dto.age
  external.user.contact.street: dto.address.street
  external.user.contact.city: dto.address.city

  # Array mapping
  external.phones: dto.phoneNumbers
  external.phones.type: dto.phoneNumbers.type
  external.phones.value: dto.phoneNumbers.number
```

### Using the Mapper

[](#using-the-mapper)

```
use Neuron\Dto\Mapper\Factory as MapperFactory;

// Create mapper
$mapperFactory = new MapperFactory('mapping.yaml');
$mapper = $mapperFactory->create();

// External data structure
$externalData = [
    'external' => [
        'username' => 'johndoe',
        'user_email' => 'john@example.com',
        'user' => [
            'profile' => [
                'age' => 30
            ],
            'contact' => [
                'street' => '123 Main St',
                'city' => 'New York'
            ]
        ],
        'phones' => [
            ['type' => 'mobile', 'value' => '+1234567890'],
            ['type' => 'home', 'value' => '+0987654321']
        ]
    ]
];

// Map to DTO
$mapper->map($dto, $externalData);

// Now DTO contains mapped data
echo $dto->username;  // 'johndoe'
echo $dto->address->street;  // '123 Main St'
echo $dto->phoneNumbers[0]->number;  // '+1234567890'
```

### Dynamic Mapping

[](#dynamic-mapping)

```
use Neuron\Dto\Mapper\Dynamic;

$mapper = new Dynamic();

// Define mappings programmatically
$mapper->addMapping('source.field1', 'target.property1');
$mapper->addMapping('source.nested.field2', 'target.property2');

// Map data
$mapper->map($dto, $sourceData);
```

Property Types
--------------

[](#property-types)

### Supported Types

[](#supported-types)

TypeDescriptionValidation`string`Text valuesLength, pattern`integer`Whole numbersRange, min, max`float`Decimal numbersRange, precision`boolean`True/false valuesType checking`array`Lists of itemsItem validation`object`Nested objectsProperty validation`dto`Referenced DTOFull DTO validation`email`Email addressesRFC compliance`url`URLsURL format`date`Date valuesDate format`date_time`Date and timeDateTime format`time`Time valuesTime format`currency`Money amountsCurrency format`uuid`UUIDsUUID v4 format`ip_address`IP addressesIPv4/IPv6`phone_number`Phone numbersInternational format`name`Person namesName validation`ein`EIN numbersUS EIN format`upc`UPC codesUPC-A format`numeric`Any numberNumeric validation`image`Image dataBase64/data URI, MIME type`base64`Base64 encoded dataBase64 format### Type Examples

[](#type-examples)

```
dto:
  # String with constraints
  username:
    type: string
    length:
      min: 3
      max: 20
    pattern: '/^[a-zA-Z0-9_]+$/'

  # Email validation
  email:
    type: email
    required: true

  # URL validation
  website:
    type: url
    required: false

  # Date with format
  birthDate:
    type: date
    format: 'Y-m-d'

  # Currency
  price:
    type: currency
    range:
      min: 0.01
      max: 999999.99

  # UUID
  userId:
    type: uuid
    required: true

  # IP Address
  clientIp:
    type: ip_address
    version: 4  # IPv4 only

  # Phone number
  phone:
    type: phone_number
    format: international

  # Image data (base64 or data URI)
  profilePicture:
    type: image
    required: false
    description: "User profile picture as base64 or data:image URI"

  # Multiple images
  gallery:
    type: array
    items:
      type: image
    description: "Array of base64 encoded images"
```

Collections
-----------

[](#collections)

### Array of Objects

[](#array-of-objects)

```
dto:
  users:
    type: array
    items:
      type: object
      properties:
        id:
          type: integer
          required: true
        name:
          type: string
          required: true
        email:
          type: email
          required: true
```

```
// Adding items to collection
$dto->users[] = (object)[
    'id' => 1,
    'name' => 'John Doe',
    'email' => 'john@example.com'
];

// Accessing collection items
foreach ($dto->users as $user) {
    echo $user->name;
}

// Collection validation
$collection = new Collection($dto->users);
if (!$collection->validate()) {
    $errors = $collection->getErrors();
}
```

### Array of Primitives

[](#array-of-primitives)

```
dto:
  tags:
    type: array
    items:
      type: string
      length:
        min: 1
        max: 20

  scores:
    type: array
    items:
      type: integer
      range:
        min: 0
        max: 100
```

Advanced Usage
--------------

[](#advanced-usage)

### Working with Images

[](#working-with-images)

The `image` type provides validation for base64-encoded image data and data URIs. It supports common image formats including JPEG, PNG, GIF, WebP, and SVG.

#### Image Property Configuration

[](#image-property-configuration)

```
dto:
  # Simple image property
  avatar:
    type: image
    required: false

  # Image with data URI support
  logo:
    type: image
    required: true
    description: "Company logo as base64 or data:image/png;base64,..."
```

#### Using Image Properties in Code

[](#using-image-properties-in-code)

```
use Neuron\Dto\Factory;

// Create DTO with image property
$factory = new Factory([
    'profile_pic' => [
        'type' => 'image',
        'required' => true
    ]
]);
$dto = $factory->create();

// Set image as base64
$imageData = base64_encode(file_get_contents('photo.jpg'));
$dto->profile_pic = $imageData;

// Or use data URI format
$dto->profile_pic = 'data:image/jpeg;base64,' . $imageData;

// Validate
$dto->validate();

// Get JSON output (image remains as base64 string)
$json = $dto->getAsJson();
```

#### Supported Image Formats

[](#supported-image-formats)

The image validator automatically detects and validates the following formats:

- **JPEG/JPG** - Detected by JPEG file signature
- **PNG** - Detected by PNG file signature
- **GIF** - Supports both GIF87a and GIF89a
- **WebP** - Modern image format
- **SVG** - XML-based vector graphics (disabled by default for security - see below)

#### Image Validation Features

[](#image-validation-features)

- **Base64 Encoding**: Validates proper base64 encoding
- **Data URI Support**: Accepts `data:image/type;base64,` format
- **MIME Type Detection**: Automatically detects image type from file signatures
- **Format Validation**: Ensures the data actually contains valid image content
- **Size Constraints**: Can be configured with maximum file size limits (via custom validator)
- **SVG Security**: SVG images are disabled by default as they can contain embedded scripts (XSS risk)

#### Security Considerations for SVG

[](#security-considerations-for-svg)

SVG images are **disabled by default** because they are XML-based and can contain:

- JavaScript code via `` tags
- Event handlers that execute JavaScript
- External resource references
- CSS that could be used for attacks

If you need to accept SVG images, you must:

1. Explicitly enable SVG support in your validator configuration
2. Sanitize SVG content before storage or display
3. Serve SVG files with appropriate Content Security Policy headers
4. Consider using a dedicated SVG sanitization library

To enable SVG support (use with caution):

```
// Create a custom validator with SVG enabled
$imageValidator = new \Neuron\Validation\IsImage(
    [], // allowed MIME types (empty = all)
    null, // max size
    true, // check image data
    true  // ALLOW SVG (security risk!)
);
```

### Complex DTO Example

[](#complex-dto-example)

```
dto:
  # User profile DTO
  profile:
    type: object
    properties:
      personalInfo:
        type: object
        required: true
        properties:
          firstName:
            type: string
            required: true
            length:
              min: 2
              max: 50
          lastName:
            type: string
            required: true
            length:
              min: 2
              max: 50
          dateOfBirth:
            type: date
            required: true
          gender:
            type: string
            enum: ['male', 'female', 'other', 'prefer_not_to_say']

      contactInfo:
        type: object
        required: true
        properties:
          emails:
            type: array
            items:
              type: object
              properties:
                type:
                  type: string
                  enum: ['personal', 'work']
                  required: true
                address:
                  type: email
                  required: true
                verified:
                  type: boolean
                  default: false

          phones:
            type: array
            items:
              type: object
              properties:
                type:
                  type: string
                  enum: ['mobile', 'home', 'work']
                number:
                  type: phone_number
                  required: true
                primary:
                  type: boolean
                  default: false

      preferences:
        type: object
        properties:
          newsletter:
            type: boolean
            default: true
          notifications:
            type: object
            properties:
              email:
                type: boolean
                default: true
              sms:
                type: boolean
                default: false
              push:
                type: boolean
                default: true

          language:
            type: string
            enum: ['en', 'es', 'fr', 'de']
            default: 'en'
```

### Custom DTO Class

[](#custom-dto-class)

```
use Neuron\Dto\Dto;

class UserDto extends Dto
{
    public function __construct()
    {
        parent::__construct();
        $this->loadConfiguration('user.yaml');
    }

    public function getFullName(): string
    {
        return $this->firstName . ' ' . $this->lastName;
    }

    public function isAdult(): bool
    {
        return $this->age >= 18;
    }

    public function toArray(): array
    {
        return [
            'username' => $this->username,
            'email' => $this->email,
            'fullName' => $this->getFullName(),
            'isAdult' => $this->isAdult()
        ];
    }
}
```

### DTO Factory with Caching

[](#dto-factory-with-caching)

```
use Neuron\Dto\Factory;

class CachedDtoFactory extends Factory
{
    private static array $cache = [];

    public function create(): Dto
    {
        $cacheKey = md5($this->configPath);

        if (!isset(self::$cache[$cacheKey])) {
            self::$cache[$cacheKey] = parent::create();
        }

        // Return deep clone to prevent shared state
        return clone self::$cache[$cacheKey];
    }
}
```

Testing
-------

[](#testing)

### Unit Testing DTOs

[](#unit-testing-dtos)

```
use PHPUnit\Framework\TestCase;
use Neuron\Dto\Factory;

class DtoTest extends TestCase
{
    private $dto;

    protected function setUp(): void
    {
        $factory = new Factory('test-dto.yaml');
        $this->dto = $factory->create();
    }

    public function testValidation(): void
    {
        $this->dto->username = 'ab';  // Too short
        $this->dto->email = 'invalid-email';

        $this->assertFalse($this->dto->validate());

        $errors = $this->dto->getErrors();
        $this->assertArrayHasKey('username', $errors);
        $this->assertArrayHasKey('email', $errors);
    }

    public function testValidData(): void
    {
        $this->dto->username = 'johndoe';
        $this->dto->email = 'john@example.com';
        $this->dto->age = 25;

        $this->assertTrue($this->dto->validate());
        $this->assertEmpty($this->dto->getErrors());
    }

    public function testNestedObjects(): void
    {
        $this->dto->address->street = '123 Main St';
        $this->dto->address->city = 'New York';

        $this->assertEquals('123 Main St', $this->dto->address->street);
        $this->assertEquals('New York', $this->dto->address->city);
    }

    public function testJsonExport(): void
    {
        $this->dto->username = 'johndoe';
        $this->dto->email = 'john@example.com';

        $json = $this->dto->getAsJson();
        $decoded = json_decode($json, true);

        $this->assertEquals('johndoe', $decoded['username']);
        $this->assertEquals('john@example.com', $decoded['email']);
    }
}
```

### Testing Mappers

[](#testing-mappers)

```
class MapperTest extends TestCase
{
    public function testDataMapping(): void
    {
        $factory = new Factory('dto.yaml');
        $dto = $factory->create();

        $mapperFactory = new MapperFactory('mapping.yaml');
        $mapper = $mapperFactory->create();

        $sourceData = [
            'external' => [
                'user_name' => 'johndoe',
                'user_email' => 'john@example.com'
            ]
        ];

        $mapper->map($dto, $sourceData);

        $this->assertEquals('johndoe', $dto->username);
        $this->assertEquals('john@example.com', $dto->email);
    }
}
```

Best Practices
--------------

[](#best-practices)

### DTO Design

[](#dto-design)

```
# Good: Clear, consistent naming
dto:
  firstName:
    type: string
    required: true
  lastName:
    type: string
    required: true
  emailAddress:
    type: email
    required: true

# Avoid: Inconsistent or unclear names
dto:
  fname:  # Too abbreviated
  last_name:  # Inconsistent style
  mail:  # Ambiguous
```

### Validation Strategy

[](#validation-strategy)

```
// Always validate before processing
if( !$dto->validate() )
{
    // Log errors
    Log::error('DTO validation failed', $dto->getErrors());

    // Return early with error response
    return new ValidationErrorResponse($dto->getErrors());
}

// Process valid data
$result = $service->process($dto);
```

### Error Handling

[](#error-handling)

```
try
{
    $dto->username = $input['username'];
    $dto->email = $input['email'];

    if( !$dto->validate() )
    {
        throw new ValidationException($dto->getErrors());
    }

    $user = $userService->create($dto);

}
catch( ValidationException $e )
{
    // Handle validation errors
    return response()->json([
        'error' => 'Validation failed',
        'details' => $e->getErrors()
    ], 422);

}
catch (PropertyNotFound $e)
{
    // Handle missing property
    return response()->json([
        'error' => 'Invalid property: ' . $e->getMessage()
    ], 400);
}
```

### Reusable DTOs

[](#reusable-dtos)

```
// Base DTO for common properties
abstract class BaseDto extends Dto
{
    protected function addTimestamps(): void
    {
        $createdAt = new Property();
        $createdAt->setName('createdAt');
        $createdAt->setType('date_time');
        $this->addProperty($createdAt);

        $updatedAt = new Property();
        $updatedAt->setName('updatedAt');
        $updatedAt->setType('date_time');
        $this->addProperty($updatedAt);
    }
}

// Specific DTO extending base
class UserDto extends BaseDto
{
    public function __construct()
    {
        parent::__construct();
        $this->loadConfiguration('user.yaml');
        $this->addTimestamps();
    }
}
```

### Performance Optimization

[](#performance-optimization)

```
// Cache DTO definitions
class DtoCache
{
    private static array $definitions = [];

    public static function getDefinition(string $config): array
    {
        if (!isset(self::$definitions[$config])) {
            self::$definitions[$config] = Yaml::parseFile($config);
        }

        return self::$definitions[$config];
    }
}

// Use lazy loading for nested objects
class LazyDto extends Dto
{
    private array $lazyProperties = [];

    public function __get(string $name)
    {
        if( isset( $this->lazyProperties[ $name ] ) )
        {
            // Load only when accessed
            $this->loadProperty($name);
        }

        return parent::__get($name);
    }
}
```

Integration Examples
--------------------

[](#integration-examples)

### API Request Validation

[](#api-request-validation)

```
class ApiController
{
    private Factory $dtoFactory;

    public function createUser(Request $request): Response
    {
        $dto = $this->dtoFactory->create('user');

        // Map request data to DTO
        $mapper = new RequestMapper();
        $mapper->map($dto, $request->all());

        // Validate
        if( !$dto->validate() )
        {
            return response()->json([
                'errors' => $dto->getErrors()
            ], 422);
        }

        // Process valid data
        $user = $this->userService->create($dto);

        return response()->json($user, 201);
    }
}
```

### Database Integration

[](#database-integration)

```
class UserRepository
{
    public function save(UserDto $dto): User
    {
        $user = new User();

        $user->username = $dto->username;
        $user->email = $dto->email;
        $user->profile = json_encode([
            'firstName' => $dto->firstName,
            'lastName' => $dto->lastName,
            'address' => [
                'street' => $dto->address->street,
                'city' => $dto->address->city,
                'state' => $dto->address->state,
                'zipCode' => $dto->address->zipCode
            ]
        ]);

        $user->save();

        return $user;
    }

    public function toDto(User $user): UserDto
    {
        $factory = new Factory('user.yaml');
        $dto = $factory->create();

        $dto->username = $user->username;
        $dto->email = $user->email;

        $profile = json_decode($user->profile, true);
        $dto->firstName = $profile['firstName'];
        $dto->lastName = $profile['lastName'];
        $dto->address->street = $profile['address']['street'];
        $dto->address->city = $profile['address']['city'];

        return $dto;
    }
}
```

More Information
----------------

[](#more-information)

- **Neuron Framework**: [neuronphp.com](http://neuronphp.com)
- **GitHub**: [github.com/neuron-php/dto](https://github.com/neuron-php/dto)
- **Packagist**: [packagist.org/packages/neuron-php/dto](https://packagist.org/packages/neuron-php/dto)

License
-------

[](#license)

MIT License - see LICENSE file for details

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance77

Regular maintenance activity

Popularity16

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~23 days

Recently: every ~6 days

Total

16

Last Release

125d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1099983?v=4)[Lee Jones](/maintainers/ljonesfl)[@ljonesfl](https://github.com/ljonesfl)

---

Top Contributors

[![ljonesfl](https://avatars.githubusercontent.com/u/1099983?v=4)](https://github.com/ljonesfl "ljonesfl (148 commits)")

---

Tags

dtodto-mapperdto-patternphp8

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/neuron-php-dto/health.svg)

```
[![Health](https://phpackages.com/badges/neuron-php-dto/health.svg)](https://phpackages.com/packages/neuron-php-dto)
```

###  Alternatives

[drupal/coder

Coder is a library to review Drupal code.

3043.6M461](/packages/drupal-coder)[romaricdrigon/metayaml

Using \[Yaml|Xml|json\] schemas files to validate \[Yaml|Xml|json\]

103306.5k8](/packages/romaricdrigon-metayaml)[ddtraceweb/smtp-validator-email

Validate your email by smtp protocol

28143.4k](/packages/ddtraceweb-smtp-validator-email)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

7310.3k29](/packages/open-dxp-opendxp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
