Examples - Grazulex/laravel-arc GitHub Wiki

๐ŸŽซ Examples Gallery

Real-world examples of Laravel Arc DTOs in action. These examples demonstrate practical usage patterns and best practices.

๐Ÿš€ Basic Examples

Simple User DTO

class UserDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: true, validation: 'max:255')]
    public string $name;

    #[Property(type: 'string', required: true, validation: 'email')]
    public string $email;

    #[Property(type: 'int', required: true, validation: 'min:0|max:150')]
    public int $age;

    #[Property(type: 'bool', required: false, default: true)]
    public bool $is_active;
}

// Usage
$user = new UserDTO([
    'name' => 'John Doe',
    'email' => '[email protected]',
    'age' => 30
]);

Product DTO with Transformations

use Grazulex\Arc\Transformers\{TrimTransformer, LowercaseTransformer};

class ProductDTO extends LaravelArcDTO
{
    #[Property(
        type: 'string',
        required: true,
        transform: [TrimTransformer::class]
    )]
    public string $name;

    #[Property(
        type: 'string',
        required: false,
        transform: [ProductSlugTransformer::class] // Custom transformer from name
    )]
    public ?string $slug;

    #[Property(type: 'float', required: true, validation: 'min:0')]
    public float $price;

    #[Property(type: 'bool', required: false, default: true)]
    public bool $is_available;
}

// Custom slug transformer that generates slug from product name
class ProductSlugTransformer extends SlugTransformer
{
    public function __construct()
    {
        parent::__construct(
            sourceField: 'name',     // Generate slug from 'name' field
            separator: '-',
            maxLength: 50
        );
    }
}

// Usage
$product = new ProductDTO([
    'name' => '  Premium Coffee Beans 2025  ',
    'price' => 29.99,
    'is_available' => true
]);

echo $product->name; // 'Premium Coffee Beans 2025' (trimmed)
echo $product->slug; // 'premium-coffee-beans-2025' (auto-generated)

๐ŸŽจ Advanced Examples

E-commerce Order System

Order Status Enum

enum OrderStatus: string
{
    case PENDING = 'pending';
    case PROCESSING = 'processing';
    case SHIPPED = 'shipped';
    case DELIVERED = 'delivered';
    case CANCELLED = 'cancelled';
}

Address DTO

class AddressDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: true, validation: 'max:255')]
    public string $street;

    #[Property(type: 'string', required: true, validation: 'max:100')]
    public string $city;

    #[Property(type: 'string', required: true, validation: 'max:20')]
    public string $postal_code;

    #[Property(
        type: 'string',
        required: true,
        validation: 'size:2',
        transform: [UppercaseTransformer::class]
    )]
    public string $country_code;
}

Order Item DTO

class OrderItemDTO extends LaravelArcDTO
{
    #[Property(type: 'int', required: true)]
    public int $product_id;

    #[Property(type: 'string', required: true, validation: 'max:255')]
    public string $product_name;

    #[Property(type: 'int', required: true, validation: 'min:1')]
    public int $quantity;

    #[Property(type: 'float', required: true, validation: 'min:0')]
    public float $unit_price;

    public function getTotalPrice(): float
    {
        return $this->quantity * $this->unit_price;
    }
}

Complete Order DTO

class OrderDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: true)]
    public string $order_number;

    #[Property(type: 'int', required: true)]
    public int $customer_id;

    #[Property(type: 'nested', class: AddressDTO::class, required: true)]
    public AddressDTO $shipping_address;

    #[Property(type: 'nested', class: AddressDTO::class, required: true)]
    public AddressDTO $billing_address;

    #[Property(type: 'collection', class: OrderItemDTO::class, required: true, validation: 'min:1')]
    public array $items;

    #[Property(type: 'enum', class: OrderStatus::class, default: OrderStatus::PENDING)]
    public OrderStatus $status;

    #[Property(type: 'date', required: false)]
    public ?Carbon $created_at;

    public function getTotalAmount(): float
    {
        return array_sum(array_map(fn($item) => $item->getTotalPrice(), $this->items));
    }

    public function getItemCount(): int
    {
        return array_sum(array_map(fn($item) => $item->quantity, $this->items));
    }
}

Blog System

User Role Enum

enum UserRole: string
{
    case ADMIN = 'admin';
    case EDITOR = 'editor';
    case AUTHOR = 'author';
    case SUBSCRIBER = 'subscriber';
}

Author DTO

class AuthorDTO extends LaravelArcDTO
{
    #[Property(type: 'int', required: true)]
    public int $id;

    #[Property(type: 'string', required: true, validation: 'max:255')]
    public string $name;

    #[Property(
        type: 'string',
        required: true,
        validation: 'email',
        transform: [TrimTransformer::class, LowercaseTransformer::class]
    )]
    public string $email;

    #[Property(type: 'enum', class: UserRole::class, default: UserRole::AUTHOR)]
    public UserRole $role;

    #[Property(type: 'string', required: false)]
    public ?string $bio;
}

Category DTO

class CategoryDTO extends LaravelArcDTO
{
    #[Property(type: 'int', required: true)]
    public int $id;

    #[Property(
        type: 'string',
        required: true,
        validation: 'max:100',
        transform: [TrimTransformer::class]
    )]
    public string $name;

    #[Property(
        type: 'string',
        required: true,
        transform: [
            TrimTransformer::class,
            LowercaseTransformer::class,
            SlugTransformer::class
        ]
    )]
    public string $slug;

    #[Property(type: 'string', required: false)]
    public ?string $description;
}

Blog Post DTO

class BlogPostDTO extends LaravelArcDTO
{
    #[Property(type: 'int', required: false)]
    public ?int $id;

    #[Property(
        type: 'string',
        required: true,
        validation: 'max:255',
        transform: [TrimTransformer::class]
    )]
    public string $title;

    #[Property(
        type: 'string',
        required: true,
        transform: [
            TrimTransformer::class,
            LowercaseTransformer::class,
            SlugTransformer::class
        ]
    )]
    public string $slug;

    #[Property(type: 'string', required: true)]
    public string $content;

    #[Property(type: 'string', required: false)]
    public ?string $excerpt;

    #[Property(type: 'nested', class: AuthorDTO::class, required: true)]
    public AuthorDTO $author;

    #[Property(type: 'collection', class: CategoryDTO::class, required: false, default: [])]
    public array $categories;

    #[Property(type: 'array', required: false, default: [])]
    public array $tags;

    #[Property(type: 'bool', required: false, default: false)]
    public bool $is_published;

    #[Property(type: 'date', required: false)]
    public ?Carbon $published_at;

    #[Property(type: 'date', required: false)]
    public ?Carbon $created_at;

    #[Property(type: 'date', required: false)]
    public ?Carbon $updated_at;
}

๐Ÿ”ง Service Layer Integration

User Service

class UserService
{
    public function __construct(
        private UserRepository $userRepository
    ) {}

    public function createUser(CreateUserDTO $dto): UserDTO
    {
        $user = $this->userRepository->create($dto->toArray());
        
        return new UserDTO([
            'id' => $user->id,
            'name' => $user->name,
            'email' => $user->email,
            'created_at' => $user->created_at
        ]);
    }

    public function updateUser(int $id, UpdateUserDTO $dto): UserDTO
    {
        $user = $this->userRepository->findOrFail($id);
        $user->update($dto->toArray());
        
        return new UserDTO($user->toArray());
    }
}

Order Service

class OrderService
{
    public function createOrder(CreateOrderDTO $dto): OrderDTO
    {
        DB::transaction(function () use ($dto) {
            // Create order
            $order = Order::create([
                'order_number' => $this->generateOrderNumber(),
                'customer_id' => $dto->customer_id,
                'status' => OrderStatus::PENDING
            ]);

            // Create order items
            foreach ($dto->items as $itemDto) {
                $order->items()->create($itemDto->toArray());
            }

            // Create addresses
            $order->shippingAddress()->create($dto->shipping_address->toArray());
            $order->billingAddress()->create($dto->billing_address->toArray());

            return $order;
        });
    }
}

๐Ÿ•๏ธ Controller Examples

API Controller

class UserController extends Controller
{
    public function store(Request $request): JsonResponse
    {
        // Validate using DTO rules
        $validated = $request->validate(CreateUserDTO::rules());
        
        // Create DTO
        $userDto = new CreateUserDTO($validated);
        
        // Process through service
        $createdUser = $this->userService->createUser($userDto);
        
        return response()->json($createdUser->toArray(), 201);
    }

    public function update(Request $request, int $id): JsonResponse
    {
        $validated = $request->validate(UpdateUserDTO::rules());
        $userDto = new UpdateUserDTO($validated);
        
        $updatedUser = $this->userService->updateUser($id, $userDto);
        
        return response()->json($updatedUser->toArray());
    }
}

Form Request Integration

class CreateUserRequest extends FormRequest
{
    public function rules(): array
    {
        return CreateUserDTO::rules();
    }

    public function toDTO(): CreateUserDTO
    {
        return new CreateUserDTO($this->validated());
    }
}

// In controller
class UserController extends Controller
{
    public function store(CreateUserRequest $request): JsonResponse
    {
        $userDto = $request->toDTO();
        $createdUser = $this->userService->createUser($userDto);
        
        return response()->json($createdUser->toArray(), 201);
    }
}

๐Ÿงช Testing Examples

DTO Testing

class UserDTOTest extends TestCase
{
    public function test_creates_user_dto_with_valid_data(): void
    {
        $data = [
            'name' => 'John Doe',
            'email' => '[email protected]',
            'age' => 30
        ];

        $user = new UserDTO($data);

        $this->assertEquals('John Doe', $user->name);
        $this->assertEquals('[email protected]', $user->email);
        $this->assertEquals(30, $user->age);
        $this->assertTrue($user->is_active); // Default value
    }

    public function test_transforms_email_to_lowercase(): void
    {
        $user = new UserDTO([
            'name' => 'John Doe',
            'email' => '[email protected]',
            'age' => 30
        ]);

        $this->assertEquals('[email protected]', $user->email);
    }

    public function test_validates_required_fields(): void
    {
        $this->expectException(ValidationException::class);

        new UserDTO([
            'email' => '[email protected]'
            // Missing required 'name' field
        ]);
    }
}

Factory Testing

class UserDTOFactory
{
    public static function make(array $attributes = []): UserDTO
    {
        return new UserDTO(array_merge([
            'name' => fake()->name(),
            'email' => fake()->email(),
            'age' => fake()->numberBetween(18, 65),
            'is_active' => true
        ], $attributes));
    }

    public static function makeInactive(): UserDTO
    {
        return self::make(['is_active' => false]);
    }

    public static function makeAdmin(): UserDTO
    {
        return self::make([
            'email' => '[email protected]',
            'role' => UserRole::ADMIN
        ]);
    }
}

// Usage in tests
class UserServiceTest extends TestCase
{
    public function test_creates_user_successfully(): void
    {
        $userDto = UserDTOFactory::make();
        
        $createdUser = $this->userService->createUser($userDto);
        
        $this->assertInstanceOf(UserDTO::class, $createdUser);
        $this->assertEquals($userDto->email, $createdUser->email);
    }
}

๐Ÿ•ฐ๏ธ Migration Examples

From v1 to v2 Syntax

Before (v1.x)

class UserDTO extends LaravelArcDTO
{
    #[DateProperty(required: true)]
    public Carbon $created_at;
    
    #[EnumProperty(enumClass: UserStatus::class)]
    public UserStatus $status;
    
    #[NestedProperty(dtoClass: AddressDTO::class)]
    public AddressDTO $address;
}

After (v2.x)

class UserDTO extends LaravelArcDTO
{
    #[Property(type: 'date', required: true)]
    public Carbon $created_at;
    
    #[Property(type: 'enum', class: UserStatus::class)]
    public UserStatus $status;
    
    #[Property(type: 'nested', class: AddressDTO::class)]
    public AddressDTO $address;
}

๐Ÿ”— Related Pages


โฌ…๏ธ Back to: Home | โžก๏ธ Next: Best Practices