DEV Community

MD ARIFUL HAQUE
MD ARIFUL HAQUE

Posted on

Simplifying Data Transfer in Laravel with DTOs

Here's a step-by-step example on how to create Data Transfer Objects (DTOs) using Laravel Data:

1. Install Laravel Data Package

To begin, install the spatie/laravel-data package using Composer. This package helps in creating DTOs and managing data efficiently.

composer require spatie/laravel-data
Enter fullscreen mode Exit fullscreen mode

2. Create a Data Transfer Object (DTO)

After installing the package, we can create a DTO class for handling data transfer. Suppose we have a User entity, and we want to create a DTO for transferring user data.

Run the following Artisan command to generate a new DTO class:

php artisan make:data UserData
Enter fullscreen mode Exit fullscreen mode

This will create a UserData class inside the App/Data directory.

3. Define Properties and Types in DTO

Now, let's edit the UserData class to define the properties and data types you expect for your DTO.

namespace App\Data;

use Spatie\LaravelData\Data;

class UserData extends Data
{
    public function __construct(
        public string $name,
        public string $email,
        public string $address,
        public ?string $phone = null  // Optional phone field
    ) {}
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The __construct method automatically assigns the incoming values to the DTO properties.
  • The ?string $phone = null indicates that the phone property is optional.

4. Use DTO in a Controller

Now that the UserData DTO is created, we can use it inside our controller to handle incoming data, transform it, and pass it between layers of the application.

namespace App\Http\Controllers;

use App\Data\UserData;
use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function store(Request $request)
    {
        // Validate the incoming request data
        $validatedData = $request->validate([
            'name' => 'required|string',
            'email' => 'required|email',
            'address' => 'required|string',
            'phone' => 'nullable|string',
        ]);

        // Create a UserData DTO
        $userData = UserData::from($validatedData);

        // You can now access $userData->name, $userData->email, etc.
        User::create([
            'name' => $userData->name,
            'email' => $userData->email,
            'address' => $userData->address,
            'phone' => $userData->phone,
        ]);

        return response()->json(['message' => 'User created successfully']);
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The UserData::from() method automatically maps the validated request data into the DTO object.
  • The DTO object can now be used to transfer data between the controller and the model (or other layers of the application).

5. Transform Data Using DTO

You can use the DTO for transforming data when returning responses as well. Let's modify the show method in the UserController to return user data through the DTO.

public function show(User $user)
{
    // Convert the User model to UserData DTO
    $userData = new UserData(
        name: $user->name,
        email: $user->email,
        address: $user->address,
        phone: $user->phone
    );

    return response()->json($userData);
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Here, we manually create a UserData DTO by passing the User model’s properties into the DTO constructor.
  • This DTO can be returned directly as a JSON response, ensuring a structured data format.

6. DTO with Collections

If you are working with collections of data, like fetching a list of users, the DTO package provides a simple method to handle collections as well.

public function index()
{
    $users = User::all();

    // Convert the collection of User models to a collection of UserData DTOs
    $userCollection = UserData::collection($users);

    return response()->json($userCollection);
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The UserData::collection($users) method maps each User model to the UserData DTO, transforming the entire collection into a list of DTOs.

7. Customize Data Transformation

The spatie/laravel-data package allows customization of the transformation, for example, renaming attributes or adding computed fields.

class UserData extends Data
{
    public function __construct(
        public string $name,
        public string $email,
        public string $address,
        public ?string $phone = null
    ) {}

    // Add a custom method to compute a full contact string
    public function fullContact(): string
    {
        return "{$this->name} ({$this->email})";
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, you can use fullContact() to get a custom formatted string when working with this DTO.

8. Data Validation and DTOs

You can add validation rules directly in the DTO using Laravel Data’s Rules feature.

namespace App\Data;

use Spatie\LaravelData\Attributes\Validation\Email;
use Spatie\LaravelData\Attributes\Validation\Required;
use Spatie\LaravelData\Data;

class UserData extends Data
{
    public function __construct(
        #[Required] public string $name,
        #[Email] public string $email,
        public string $address,
        public ?string $phone = null
    ) {}
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • With validation attributes like #[Required] and #[Email], you can enforce validation directly on the DTO level, streamlining the process even further.

Conclusion

In this hands-on tutorial, we explored how to create and use Data Transfer Objects (DTOs) using Laravel Data. With the DTO pattern, you can cleanly manage and transform your data, ensuring separation of concerns between your application's layers, while making code easier to maintain. The spatie/laravel-data package simplifies DTO management, offering easy-to-use features for both developers and large-scale applications.

Top comments (0)