Copied!
Laravel

Mastering DTOs in Laravel — Why They Matter and How to Use Them Effectively

mastering-dtos-in-laravel
Shahroz Javed
Oct 22, 2025 . 28 views

Table Of Contents

 

Introduction

In modern Laravel applications, keeping code organized, reusable, and easy to maintain is essential. One way to achieve this is by using a DTO (Data Transfer Object). DTOs act as structured containers that carry data between layers (like requests, services, or models) — without business logic or side effects.

Think of a DTO as a clean “data package” that ensures consistency and separation of concerns when handling data from different sources such as forms, APIs, or database models.

💡 Related: You may also like our post on Service Layer Pattern in Laravel.

Why DTOs Are Important

Without DTOs, controllers or services often get messy — juggling request validation, business logic, and data transformations all at once. Using a DTO brings:

⚠️ Note: DTOs don’t replace Eloquent models or form requests — they complement them by providing a layer between raw data and model persistence.

When to Use DTOs

You should consider using a DTO whenever:

Simple Example of a DTO in Laravel

Here’s a practical example from a vehicle rental system, showing a clean DTO implementation for VehicleRentalHistory.

Step 1: Create a DTO Class

<?php

namespace App\DTOs;

use Illuminate\Http\Request;
use Carbon\Carbon;

class VehicleRentalHistoryDTO
{
    public int $vehicle_id;
    public ?int $user_id;
    public string $rental_start;
    public ?string $rental_end;
    public ?float $total_amount;
    public float $discount_applied;
    public ?float $final_amount;
    public string $status;
    public ?string $notes;

    public function __construct(array $data)
    {
        $this->vehicle_id = $data['vehicle_id'];
        $this->user_id = $data['user_id'] ?? null;
        $this->rental_start = $data['rental_start'];
        $this->rental_end = $data['rental_end'] ?? null;
        $this->total_amount = isset($data['total_amount']) ? (float) $data['total_amount'] : null;
        $this->discount_applied = $data['discount_applied'] ?? 0;
        $this->final_amount = isset($data['final_amount']) ? (float) $data['final_amount'] : null;
        $this->status = $data['status'] ?? 'reserved';
        $this->notes = $data['notes'] ?? null;
    }

    public static function fromArray(array $data): self
    {
        return new self($data);
    }

    public static function fromRequest(Request $request): self
    {
        return new self($request->validated() ?? $request->all());
    }

    public function toArray(): array
    {
        return [
            'vehicle_id' => $this->vehicle_id,
            'user_id' => $this->user_id,
            'rental_start' => $this->rental_start,
            'rental_end' => $this->rental_end,
            'total_amount' => $this->total_amount,
            'discount_applied' => $this->discount_applied,
            'final_amount' => $this->final_amount,
            'status' => $this->status,
            'notes' => $this->notes,
        ];
    }
}

This DTO handles clean data mapping between incoming requests and database models. Notice how it also includes helper static methods like fromRequest() and toArray() for convenience.

Step 2: Use DTO in Your Controller

In your controller, instead of working directly with request data, you can now use the DTO:

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\VehicleRentalHistory;
use App\DTOs\VehicleRentalHistoryDTO;
use App\Http\Requests\StoreVehicleRentalHistoryRequest;
use Illuminate\Http\JsonResponse;

class VehicleRentalHistoryController extends Controller
{
    public function store(StoreVehicleRentalHistoryRequest $request): JsonResponse
    {
        $dto = VehicleRentalHistoryDTO::fromRequest($request);
        $record = VehicleRentalHistory::create($dto->toArray());

        return response()->json([
            'message' => 'Rental history record created successfully.',
            'data' => $record,
        ], 201);
    }
}

The controller now looks cleaner and only focuses on the workflow — the DTO takes care of structuring data for the model.

Benefits of Using DTOs in Laravel

⚠️ Tip: DTOs shine when used with service layers, repository patterns, or API endpoints that require complex input transformations.

Conclusion

DTOs are a small but powerful addition to your Laravel architecture. They bridge the gap between raw input and business logic, promoting cleaner and more maintainable code. If you want your Laravel apps to scale smoothly and stay organized, start introducing DTOs into your workflow today.

💡 Related: Explore How to Implement Repository Pattern in Laravel for an even cleaner architecture.

29 Shares

Similar Posts