Skip to content

Generated DTO Structure

Jean-Marc Strauven edited this page Aug 6, 2025 · 1 revision

πŸ—οΈ Generated DTO Structure

Understanding what Laravel Arc generates helps you leverage the full power of your DTOs. This guide shows you exactly what code is created from your YAML definitions.

The Generation Process

YAML Definition β†’ Laravel Arc Parser β†’ Generated PHP Class
      ↓                    ↓                    ↓
Schema Rules    β†’    Code Generation    β†’  Type-Safe DTO

Laravel Arc transforms your YAML into modern PHP 8.3+ classes with full type safety and rich functionality.

Basic Generated Structure

From This YAML:

header:
  class: UserDto
  namespace: App\DTOs

fields:
  name:
    type: string
    required: true
    max_length: 255
    
  email:
    type: email
    required: true
    
  age:
    type: integer
    min: 18
    max: 120

Generates This PHP:

<?php

declare(strict_types=1);

namespace App\DTOs;

use Grazulex\LaravelArc\Support\Traits\ValidatesData;
use Grazulex\LaravelArc\Support\Traits\ConvertsData;

/**
 * User data transfer object
 * 
 * @property string $name User's name
 * @property string $email User's email address  
 * @property int $age User's age
 */
final readonly class UserDto
{
    use ValidatesData, ConvertsData;

    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}

    /**
     * Get validation rules for this DTO
     */
    public static function validationRules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email'],
            'age' => ['required', 'integer', 'min:18', 'max:120'],
        ];
    }

    /**
     * Create DTO from array with validation
     */
    public static function fromValidated(array $data): static
    {
        validator($data, static::validationRules())->validate();
        return static::from($data);
    }

    /**
     * Create DTO from Laravel request
     */
    public static function fromRequest(\Illuminate\Http\Request $request): static
    {
        return static::fromValidated($request->all());
    }
}

Core Components

1. Class Declaration

Laravel Arc generates modern PHP classes with:

declare(strict_types=1);  // Strict typing
final readonly class UserDto  // Immutable by default

Key Features:

  • declare(strict_types=1) - Full type enforcement
  • final - Prevents inheritance (configurable)
  • readonly - Immutable properties (configurable)
  • Proper namespace and imports

2. Constructor Properties

Properties are defined as constructor parameters:

public function __construct(
    public string $name,           // Required field
    public string $email,          // Required field
    public int $age,              // Required field
    public ?string $nickname = null, // Optional field
    public bool $active = true,    // Field with default
) {}

Property Features:

  • Type hints from YAML field types
  • Visibility (public by default)
  • Readonly for immutability
  • Nullable when specified
  • Default values from YAML

3. Validation Rules

Automatic Laravel validation generation:

public static function validationRules(): array
{
    return [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'email', 'unique:users,email'],
        'age' => ['integer', 'min:18', 'max:120'],
        'status' => ['string', 'in:active,inactive,pending'],
    ];
}

Rules are generated from YAML field definitions automatically.

4. Helper Traits

Laravel Arc includes powerful traits:

use ValidatesData;    // Validation methods
use ConvertsData;     // Data conversion methods
use HasTimestamps;    // Timestamp functionality (when trait added)

Advanced Generated Features

With Transformers

YAML with transformers:

fields:
  name:
    type: string
    transformers: [trim, title_case]
  email:
    type: email
    transformers: [trim, lowercase]

Generates transformation in factory methods:

public static function from(array $data): static
{
    // Apply transformers
    $data['name'] = TitleCase::transform(Trim::transform($data['name'] ?? ''));
    $data['email'] = Lowercase::transform(Trim::transform($data['email'] ?? ''));
    
    return new static(
        name: $data['name'],
        email: $data['email'],
    );
}

With Behavioral Traits

YAML with traits:

header:
  traits: [HasTimestamps, HasUuid]

Generates additional properties and methods:

final readonly class UserDto
{
    use ValidatesData, ConvertsData, HasTimestamps, HasUuid;

    public function __construct(
        public string $name,
        public string $email,
        public string $uuid,              // From HasUuid
        public ?\DateTime $created_at = null,  // From HasTimestamps
        public ?\DateTime $updated_at = null,  // From HasTimestamps
    ) {}

    // Additional trait methods available:
    // $dto->getUuid()
    // $dto->touch()
    // $dto->wasRecentlyCreated()
}

With Relations

YAML with nested DTOs:

fields:
  name: { type: string }
  
relations:
  address:
    type: has_one
    class: AddressDto

Generates nested DTO handling:

public function __construct(
    public string $name,
    public ?AddressDto $address = null,
) {}

public static function from(array $data): static
{
    // Handle nested DTO creation
    $address = isset($data['address']) 
        ? AddressDto::from($data['address']) 
        : null;
        
    return new static(
        name: $data['name'],
        address: $address,
    );
}

Trait Integration

ValidatesData Trait

Provides validation methods:

// Validate data against DTO rules
UserDto::validate($data);  // Returns Validator instance

// Create with validation
$user = UserDto::fromValidated($data);

// Validate current DTO data
$user->isValid();  // boolean
$user->getValidationErrors();  // array

ConvertsData Trait

Provides conversion methods:

// Export to different formats
$user->toArray();     // Array
$user->toJson();      // JSON string
$user->toXml();       // XML string
$user->toCsv();       // CSV string
$user->toYaml();      // YAML string

// Collection methods
$user->collect();     // Laravel Collection
$user->pipe($callback); // Pipeline processing

Real-World Generated Example

Complex YAML Definition

header:
  class: OrderDto
  namespace: App\DTOs\Orders
  traits: [HasTimestamps, HasUuid, HasStatus]
  description: "Complete order data transfer object"

trait_config:
  HasStatus:
    default: "pending"
    states: [pending, processing, shipped, delivered, cancelled]

fields:
  # Customer information
  customer_name:
    type: string
    required: true
    max_length: 255
    transformers: [trim, title_case]
    
  customer_email:
    type: email
    required: true
    transformers: [trim, lowercase]
    
  # Order details
  order_number:
    type: string
    required: true
    unique: true
    pattern: "^ORD-[0-9]{6}$"
    
  total_amount:
    type: decimal
    precision: 10
    scale: 2
    min: 0
    
  currency:
    type: string
    default: "USD"
    enum: [USD, EUR, GBP, CAD]
    
  # Shipping
  shipping_address:
    type: text
    required: true
    max_length: 500
    
relations:
  items:
    type: has_many
    class: OrderItemDto

export:
  formats: [json, xml]
  exclude: [internal_notes]

Generated OrderDto Class

<?php

declare(strict_types=1);

namespace App\DTOs\Orders;

use Grazulex\LaravelArc\Support\Traits\ValidatesData;
use Grazulex\LaravelArc\Support\Traits\ConvertsData;
use Grazulex\LaravelArc\Support\Traits\HasTimestamps;
use Grazulex\LaravelArc\Support\Traits\HasUuid;
use Grazulex\LaravelArc\Support\Traits\HasStatus;
use Illuminate\Support\Collection;

/**
 * Complete order data transfer object
 * 
 * @property string $customer_name Customer's full name
 * @property string $customer_email Customer's email address
 * @property string $order_number Unique order identifier
 * @property float $total_amount Order total amount
 * @property string $currency Order currency
 * @property string $shipping_address Shipping address
 * @property Collection<OrderItemDto> $items Order items
 * @property string $uuid Unique identifier
 * @property string $status Order status
 * @property \DateTime|null $created_at Creation timestamp
 * @property \DateTime|null $updated_at Last update timestamp
 */
final readonly class OrderDto
{
    use ValidatesData, ConvertsData, HasTimestamps, HasUuid, HasStatus;

    public function __construct(
        public string $customer_name,
        public string $customer_email,
        public string $order_number,
        public float $total_amount,
        public string $currency = 'USD',
        public string $shipping_address,
        public Collection $items,
        public string $uuid,
        public string $status = 'pending',
        public ?\DateTime $created_at = null,
        public ?\DateTime $updated_at = null,
    ) {}

    /**
     * Get validation rules for this DTO
     */
    public static function validationRules(): array
    {
        return [
            'customer_name' => ['required', 'string', 'max:255'],
            'customer_email' => ['required', 'email'],
            'order_number' => ['required', 'string', 'unique:orders,order_number', 'regex:/^ORD-[0-9]{6}$/'],
            'total_amount' => ['required', 'numeric', 'decimal:0,2', 'min:0'],
            'currency' => ['string', 'in:USD,EUR,GBP,CAD'],
            'shipping_address' => ['required', 'string', 'max:500'],
            'items' => ['required', 'array'],
            'items.*' => ['array'], // OrderItemDto validation
        ];
    }

    /**
     * Create DTO from array with transformations
     */
    public static function from(array $data): static
    {
        // Apply transformers
        $data['customer_name'] = TitleCase::transform(Trim::transform($data['customer_name'] ?? ''));
        $data['customer_email'] = Lowercase::transform(Trim::transform($data['customer_email'] ?? ''));
        
        // Handle nested DTOs
        $items = collect($data['items'] ?? [])
            ->map(fn($item) => OrderItemDto::from($item));
            
        // Generate UUID if not provided
        $data['uuid'] ??= Str::uuid()->toString();
        
        return new static(
            customer_name: $data['customer_name'],
            customer_email: $data['customer_email'],
            order_number: $data['order_number'],
            total_amount: (float) $data['total_amount'],
            currency: $data['currency'] ?? 'USD',
            shipping_address: $data['shipping_address'],
            items: $items,
            uuid: $data['uuid'],
            status: $data['status'] ?? 'pending',
            created_at: isset($data['created_at']) ? new \DateTime($data['created_at']) : null,
            updated_at: isset($data['updated_at']) ? new \DateTime($data['updated_at']) : null,
        );
    }

    /**
     * Export to array (respects export configuration)
     */
    public function toArray(): array
    {
        return [
            'customer_name' => $this->customer_name,
            'customer_email' => $this->customer_email,
            'order_number' => $this->order_number,
            'total_amount' => $this->total_amount,
            'currency' => $this->currency,
            'shipping_address' => $this->shipping_address,
            'items' => $this->items->map->toArray()->all(),
            'uuid' => $this->uuid,
            'status' => $this->status,
            'created_at' => $this->created_at?->format('c'),
            'updated_at' => $this->updated_at?->format('c'),
            // 'internal_notes' excluded per export config
        ];
    }

    /**
     * Get total order value including tax
     */
    public function getTotalWithTax(float $taxRate = 0.1): float
    {
        return $this->total_amount * (1 + $taxRate);
    }

    /**
     * Get order items count
     */
    public function getItemsCount(): int
    {
        return $this->items->count();
    }

    /**
     * Check if order can be cancelled
     */
    public function canBeCancelled(): bool
    {
        return in_array($this->status, ['pending', 'processing']);
    }
}

Generated Method Categories

1. Factory Methods

static::from(array $data)           // Create from array
static::fromValidated(array $data)  // Create with validation
static::fromRequest(Request $request) // Create from HTTP request
static::fromJson(string $json)      // Create from JSON

2. Validation Methods

static::validationRules()           // Get Laravel validation rules
static::validate(array $data)       // Validate data
$dto->isValid()                     // Check if DTO is valid
$dto->getValidationErrors()         // Get validation errors

3. Conversion Methods

$dto->toArray()                     // Convert to array
$dto->toJson()                      // Convert to JSON
$dto->toXml()                       // Convert to XML
$dto->toCsv()                       // Convert to CSV
$dto->toYaml()                      // Convert to YAML

4. Collection Methods

$dto->collect()                     // Wrap in Laravel Collection
$dto->pipe(callable $callback)      // Pipeline processing
$dto->map(callable $callback)       // Map over properties

5. Utility Methods

$dto->with(array $changes)          // Create copy with changes
$dto->only(array $keys)             // Get subset of properties
$dto->except(array $keys)           // Get all except specified
$dto->isEmpty()                     // Check if empty
$dto->isNotEmpty()                  // Check if not empty

Performance Optimizations

Laravel Arc generates optimized code:

1. Minimal Object Creation

// Efficient constructor with typed parameters
public function __construct(
    public readonly string $name,    // Direct property assignment
    public readonly int $age,        // No setter overhead
) {}

2. Lazy Loading for Relations

// Relations loaded only when accessed
public function getAddress(): ?AddressDto
{
    return $this->address ??= AddressDto::from($this->addressData);
}

3. Cached Validation Rules

private static ?array $cachedRules = null;

public static function validationRules(): array
{
    return self::$cachedRules ??= [
        // ... validation rules
    ];
}

What's Next?

Now that you understand generated DTO structure:


Understanding the generated code helps you leverage the full power of Laravel Arc DTOs. Every YAML definition becomes a rich, type-safe PHP class. πŸ—οΈ

πŸš€ Laravel Arc Wiki

🏠 Home

πŸš€ Getting Started

πŸ“š Core Concepts

πŸ—οΈ Advanced Features

βš™οΈ Configuration & CLI

🌐 Real-World Examples


🎯 Key Concepts

YAML β†’ DTO β†’ Type-Safe Code

Laravel Arc transforms your YAML definitions into powerful PHP DTOs with automatic validation, field transformers, and behavioral traits.

πŸ”— Quick Links

Clone this wiki locally