-
-
Notifications
You must be signed in to change notification settings - Fork 0
Complete Examples
Jean-Marc Strauven edited this page Aug 6, 2025
·
1 revision
This page contains all the practical examples you need to master Laravel Arc, from simple DTOs to complex nested structures and API integrations.
- Basic Examples
- Behavioral Traits Examples
- Nested DTO Examples
- API Controller Examples
- Field Transformers Examples
- Advanced Examples
# Basic user DTO with validation and transformers
header:
dto: UserDTO
table: users
model: App\Models\User
namespace: App\DTO
traits:
- HasUuid
- HasTimestamps
use:
- App\DTO\ProfileDTO
extends: BaseDTO
fields:
name:
type: string
required: true
validation: [required, string, min:2, max:100]
transformers: [trim, title_case]
email:
type: string
required: true
validation: [required, email, unique:users]
transformers: [trim, lowercase]
email_verified_at:
type: datetime
required: false
validation: [nullable, date]
# Profile as nested DTO
profile:
type: dto
dto: ProfileDTO
required: false
relations:
posts:
type: hasMany
target: App\Models\Post
roles:
type: belongsToMany
target: App\Models\Role
# E-commerce product DTO
header:
dto: ProductDTO
namespace: App\DTO\Catalog
traits: [HasTimestamps, HasUuid, HasSlug]
fields:
name:
type: string
required: true
max_length: 255
transformers: [trim, title_case]
slug:
type: string
unique: true
transformers: [slugify]
description:
type: text
max_length: 2000
transformers: [trim, strip_tags]
price:
type: decimal
precision: 10
scale: 2
min: 0
sku:
type: string
required: true
unique: true
transformers: [trim, uppercase]
category_id:
type: integer
required: true
is_active:
type: boolean
default: true
stock_quantity:
type: integer
min: 0
default: 0
# Example showing all behavioral traits working together
header:
dto: AdvancedUserDTO
model: App\Models\User
namespace: App\DTO
traits:
- HasTimestamps # Adds created_at, updated_at
- HasUuid # Adds UUID field
- HasSoftDeletes # Adds deleted_at, restore methods
- HasTagging # Adds tags field and tag methods
- HasAuditing # Adds creator_id, updater_id tracking
- HasCaching # Adds cache methods
- HasSlug # Adds slug field from name
- HasStatus # Adds status field with state management
- ValidatesData # Adds validation methods
- ConvertsData # Adds export methods
fields:
name:
type: string
required: true
validation: [required, string, min:2, max:100]
transformers: [trim, title_case]
email:
type: string
required: true
validation: [required, email, unique:users]
transformers: [trim, lowercase]
bio:
type: text
required: false
validation: [nullable, string, max:1000]
transformers: [trim]
# Traits configuration
trait_options:
HasSlug:
source_field: name
unique: true
HasStatus:
default: "active"
allowed: [active, inactive, pending, banned]
HasCaching:
ttl: 3600
# HasTimestamps only
---
header:
dto: UserWithTimestampsDTO
namespace: App\DTO
traits: [HasTimestamps]
fields:
name: { type: string, required: true }
email: { type: string, required: true }
# HasTimestamps adds:
# - created_at: datetime field
# - updated_at: datetime field
# - touch() method
# - wasRecentlyCreated() method
---
# HasStatus with custom states
header:
dto: OrderStatusDTO
namespace: App\DTO
traits: [HasStatus]
trait_config:
HasStatus:
default: "pending"
states: [pending, processing, shipped, delivered, cancelled]
transitions:
pending: [processing, cancelled]
processing: [shipped, cancelled]
shipped: [delivered]
fields:
order_number: { type: string, required: true }
# Complex nested DTO for e-commerce orders
header:
dto: OrderDTO
table: orders
model: App\Models\Order
namespace: App\DTO\Ecommerce
traits:
- HasTimestamps
- HasUuid
- HasSoftDeletes
- HasVersioning
- HasAuditing
fields:
order_number:
type: string
required: true
validation: [required, string, unique:orders, size:12]
status:
type: enum
values: [pending, processing, shipped, delivered, cancelled, refunded]
default: pending
# Customer as nested DTO
customer:
type: dto
dto: CustomerDTO
required: true
# Collection of order items
items:
type: array
items:
type: dto
dto: OrderItemDTO
min_items: 1
# Billing address
billing_address:
type: dto
dto: AddressDTO
required: true
# Optional shipping address
shipping_address:
type: dto
dto: AddressDTO
required: false
# Payment information
payment:
type: dto
dto: PaymentDTO
required: true
# Totals
subtotal:
type: decimal
precision: 10
scale: 2
tax_amount:
type: decimal
precision: 10
scale: 2
total_amount:
type: decimal
precision: 10
scale: 2
# Customer DTO
---
header:
dto: CustomerDTO
namespace: App\DTO\Ecommerce
traits: [HasTimestamps]
fields:
customer_id:
type: integer
required: true
name:
type: string
required: true
validation: [required, string, max:255]
email:
type: email
required: true
phone:
type: string
nullable: true
validation: [nullable, string, max:20]
---
# Address DTO
header:
dto: AddressDTO
namespace: App\DTO\Ecommerce
fields:
street:
type: string
required: true
max_length: 255
city:
type: string
required: true
max_length: 100
state:
type: string
required: true
max_length: 100
postal_code:
type: string
required: true
pattern: "^[0-9]{5}(-[0-9]{4})?$"
country:
type: dto
dto: CountryDTO
required: true
---
# Country DTO
header:
dto: CountryDTO
namespace: App\DTO\Ecommerce
fields:
code:
type: string
required: true
validation: [required, string, size:2]
name:
type: string
required: true
max_length: 100
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api;
use App\DTO\UserDTO;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
final class UserController extends Controller
{
/**
* Display a listing of users with DTO transformation
*/
public function index(Request $request): JsonResponse
{
$users = User::query()
->when($request->search, function ($query, $search) {
$query->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
})
->when($request->status, function ($query, $status) {
$query->where('status', $status);
})
->paginate($request->per_page ?? 15);
// Transform to DTOs with behavioral traits
$userDtos = $users->getCollection()->map(function ($user) {
return UserDTO::fromModel($user);
});
return response()->json([
'data' => $userDtos,
'pagination' => [
'current_page' => $users->currentPage(),
'last_page' => $users->lastPage(),
'per_page' => $users->perPage(),
'total' => $users->total(),
],
]);
}
/**
* Store a newly created user with comprehensive validation
*/
public function store(Request $request): JsonResponse
{
// Use ValidatesData trait for validation
if (UserDTO::fails($request->all())) {
return response()->json([
'message' => 'Validation failed',
'errors' => UserDTO::validator($request->all())->errors(),
], 422);
}
// Create user with validated data
$validated = UserDTO::validate($request->all());
$user = User::create($validated);
// Convert to DTO and demonstrate behavioral traits
$userDto = UserDTO::fromModel($user);
// Use HasTagging trait if available
if (method_exists($userDto, 'addTag')) {
$userDto = $userDto->addTag('new_user');
}
return response()->json([
'message' => 'User created successfully',
'user' => $userDto->toArray(),
], 201);
}
/**
* Display the specified user
*/
public function show(User $user): JsonResponse
{
$userDto = UserDTO::fromModel($user);
return response()->json([
'user' => $userDto->toArray(),
]);
}
/**
* Update the specified user
*/
public function update(Request $request, User $user): JsonResponse
{
// Validate with DTO
$validated = UserDTO::validate($request->all());
// Update model
$user->update($validated);
// Convert to DTO
$userDto = UserDTO::fromModel($user->fresh());
// Use HasTimestamps trait
if (method_exists($userDto, 'touch')) {
$userDto->touch(); // Updates updated_at
}
return response()->json([
'message' => 'User updated successfully',
'user' => $userDto->toArray(),
]);
}
/**
* Bulk operations example
*/
public function bulkStore(Request $request): JsonResponse
{
$users = collect($request->users)->map(function ($userData) {
// Validate each user
$validated = UserDTO::validate($userData);
$user = User::create($validated);
return UserDTO::fromModel($user);
});
return response()->json([
'message' => 'Users created successfully',
'users' => $users->toArray(),
'count' => $users->count(),
], 201);
}
/**
* Export functionality
*/
public function export(Request $request): JsonResponse
{
$users = User::all();
$userDtos = $users->map(fn($user) => UserDTO::fromModel($user));
// Use DTO export capabilities
$format = $request->format ?? 'json';
switch ($format) {
case 'csv':
$content = $userDtos->toCsv();
break;
case 'xml':
$content = $userDtos->toXml();
break;
default:
$content = $userDtos->toJson();
}
return response($content)
->header('Content-Type', "application/{$format}")
->header('Content-Disposition', "attachment; filename=users.{$format}");
}
}
// routes/api.php
Route::prefix('v1')->group(function () {
Route::apiResource('users', UserController::class);
Route::post('users/bulk', [UserController::class, 'bulkStore']);
Route::get('users/export', [UserController::class, 'export']);
Route::apiResource('orders', OrderController::class);
Route::get('orders/{order}/items', [OrderController::class, 'items']);
});
# Field transformers example showing data cleaning
header:
dto: ContactFormDTO
namespace: App\DTO\Contact
fields:
# Basic string transformations
name:
type: string
required: true
transformers: [trim, title_case]
# " john doe " β "John Doe"
email:
type: email
required: true
transformers: [trim, lowercase]
# " [email protected] " β "[email protected]"
# URL and slug transformations
website:
type: url
nullable: true
transformers: [trim, normalize_url, add_http]
# "example.com" β "http://example.com"
slug:
type: string
transformers: [trim, lowercase, slugify]
# "Hello World!" β "hello-world"
# Phone number normalization
phone:
type: string
nullable: true
transformers: [normalize_phone]
# "(555) 123-4567" β "+15551234567"
# Text cleaning
message:
type: text
required: true
transformers: [trim, strip_tags, normalize_whitespace]
# "<p>Hello\n\nworld</p>" β "Hello world"
# Numeric transformations
price:
type: decimal
precision: 2
transformers: [round_to_precision]
# 19.999 β 19.99
# Array transformations
tags:
type: array
items: { type: string }
transformers: [trim_array, lowercase_array, unique_array]
# [" TAG1 ", "tag2", "TAG1"] β ["tag1", "tag2"]
# Complex transformation chains
header:
dto: ProductDataDTO
namespace: App\DTO\Catalog
fields:
# Product name with multiple transformations
name:
type: string
required: true
transformers: [trim, strip_tags, title_case, truncate:100]
# " <b>awesome product name</b> " β "Awesome Product Name"
# SEO-friendly slug
slug:
type: string
unique: true
transformers: [trim, lowercase, slugify, ensure_unique]
# " My Awesome Product! " β "my-awesome-product" or "my-awesome-product-2"
# Clean description
description:
type: text
transformers: [trim, strip_tags, normalize_whitespace, smart_truncate:500]
# Format SKU
sku:
type: string
required: true
transformers: [trim, uppercase, alphanumeric_dash_only]
# " abc-123 " β "ABC-123"
# Enum field usage examples
header:
dto: OrderStatusDTO
namespace: App\DTO
fields:
# Simple enum
status:
type: enum
values: [pending, confirmed, shipped, delivered, cancelled]
default: pending
# Priority enum with validation
priority:
type: enum
values: [low, medium, high, urgent]
default: medium
validation: [required, in:low,medium,high,urgent]
# Payment status with descriptions
payment_status:
type: enum
values:
pending: "Payment Pending"
processing: "Processing Payment"
completed: "Payment Completed"
failed: "Payment Failed"
refunded: "Payment Refunded"
default: pending
# Complex validation scenarios
header:
dto: UserRegistrationDTO
namespace: App\DTO\Auth
fields:
# Username with custom validation
username:
type: string
required: true
validation:
- required
- string
- min:3
- max:20
- unique:users
- regex:/^[a-zA-Z0-9_]+$/
transformers: [trim, lowercase]
# Password with strength requirements
password:
type: string
required: true
validation:
- required
- string
- min:8
- confirmed
- regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[\d\x]).*$/
# Email with domain restriction
email:
type: email
required: true
validation:
- required
- email:rfc,dns
- unique:users
- ends_with:@company.com,@partner.com
transformers: [trim, lowercase]
# Age with business rules
age:
type: integer
required: true
validation:
- required
- integer
- min:18
- max:65
# File upload validation
avatar:
type: file
nullable: true
validation:
- nullable
- image
- max:2048
- mimes:jpeg,png,jpg,gif
- dimensions:min_width=100,min_height=100
<?php
// Collection processing with DTOs
class UserService
{
public function processUserBatch(array $userData): array
{
$results = [
'successful' => collect(),
'failed' => collect(),
'duplicates' => collect()
];
collect($userData)->each(function($data) use (&$results) {
try {
// Check for duplicates
if (User::where('email', $data['email'])->exists()) {
$results['duplicates']->push($data);
return;
}
// Validate and create DTO
$userDto = UserDTO::fromValidated($data);
// Create user
$user = User::create($userDto->toArray());
$results['successful']->push(UserDTO::fromModel($user));
} catch (ValidationException $e) {
$results['failed']->push([
'data' => $data,
'errors' => $e->errors()
]);
}
});
return [
'processed' => count($userData),
'successful' => $results['successful']->count(),
'failed' => $results['failed']->count(),
'duplicates' => $results['duplicates']->count(),
'errors' => $results['failed']->toArray()
];
}
}
<?php
namespace App\Http\Requests;
use App\DTO\UserDTO;
use Illuminate\Foundation\Http\FormRequest;
class CreateUserRequest extends FormRequest
{
public function rules(): array
{
return UserDTO::validationRules();
}
public function toDto(): UserDTO
{
return UserDTO::fromValidated($this->validated());
}
}
// In controller
public function store(CreateUserRequest $request): JsonResponse
{
$userDto = $request->toDto();
$user = User::create($userDto->toArray());
return response()->json([
'user' => UserDTO::fromModel($user)->toArray()
], 201);
}
<?php
namespace App\Services;
use App\DTO\OrderDTO;
use App\Models\Order;
class OrderService
{
public function createOrder(OrderDTO $orderDto): Order
{
// Business logic with type-safe DTO
$this->validateInventory($orderDto->items);
$this->calculateTotals($orderDto);
// Create order
$order = Order::create($orderDto->toArray());
// Process items
foreach ($orderDto->items as $itemDto) {
$order->items()->create($itemDto->toArray());
}
return $order;
}
private function validateInventory(Collection $items): void
{
$items->each(function($itemDto) {
if ($itemDto->quantity > $this->getAvailableStock($itemDto->product_id)) {
throw new InsufficientStockException($itemDto->product_id);
}
});
}
}
// config/dto.php
return [
'definitions_path' => resource_path('arc'),
'output_path' => app_path('DTOs'),
// Custom namespace mapping
'namespace_mapping' => [
'api' => 'App\\Http\\DTOs',
'domain' => 'App\\Domain\\DTOs',
'integration' => 'App\\Integration\\DTOs',
],
// Generation options
'generation' => [
'strict_types' => true,
'readonly_properties' => true,
'final_classes' => true,
'add_docblocks' => true,
],
];
This complete examples collection provides practical, copy-paste code for every Laravel Arc feature. Use these examples as starting points for your own DTOs.
Laravel Arc - Generate Type-Safe DTOs from YAML Definitions
π Home | π Get Started | π Examples | βοΈ Config
From YAML to Type-Safe Code - Made with β€οΈ for the Laravel community
π Home
- π Understanding YAML Structure
- π·οΈ Field Types
- π Field Transformers
- π Behavioral Traits
YAML β DTO β Type-Safe Code
Laravel Arc transforms your YAML definitions into powerful PHP DTOs with automatic validation, field transformers, and behavioral traits.
- π Get Started - Create your first DTO in 5 minutes
- π All Examples - Copy-paste ready examples
- β‘ Commands - CLI reference