Mastering Data Transfer Objects in Laravel
Time to read: 7 minutes
Mastering Data Transfer Objects in Laravel
Data Transfer Objects (DTOs) are a crucial component in modern software architecture, enhancing data handling and management across different layers of an application. In Laravel, DTOs offer a structured and efficient way to manage data transfer between the application's internal components and external interfaces. This tutorial will guide you through mastering DTOs in Laravel, from basic concepts to advanced implementations.
Prerequisites
The following technologies are required to follow along effectively
PHP 8.2
Composer installed globally
Access to a MySQL database
Prior experience with Laravel and PHP would be ideal
Understanding DTOs
A DTO is, essentially, a plain old PHP class that holds a bunch of data. It doesn't have any business logic. Its main purpose is to transfer data between layers of your application, such as from a controller to a service, or from a service to a view.
Benefits of using DTOs in web development
Below, I have outlined some of the more obvious reasons why you might want to use DTOs in development.
Encapsulation: Think of DTOs as boxes that connect related objects. Just as you can fit all your socks in one drawer, DTOs collect the same pieces of data. This makes data easier to handle and understand.
Type security: DTOs help ensure that the data being sent to your application is of the correct type. For example, when expecting a number, DTOs help ensure that you don’t accidentally get a text string. This keeps your data clean and reliable.
Reduced complexity: DTOs take a complex data structure and simplify it, making it easier to work with the data. In complex web development projects, sometimes data can feel like a tangled headset; DTOs help release them.
Improved performance: DTOs can speed up your application by ensuring that only important pieces of data are moved around. It’s like choosing to carry only what you need in your backpack rather than everything; It is very small and easy to handle.
Let's create a DTO class from scratch in Laravel without relying on external packages. This approach gives you more control and can be a great learning exercise.
Create a new Laravel project
Let's start by creating a Laravel project using Composer like so:
After creating the project, navigate into the project directory using the command below.
Set up the database
Change the database settings (DB_
) in .env with your database details to work with any database of your choice; I used MySQL for this tutorial.
Generate database seed data
Our freshly created Laravel application is shipped with a User
model, User migration, and seeder out of the proverbial box. Let's run our migration and seed the database to generate some test data for our DTO class. In database/seeder/DatabaseSeeder.php, replace the content with the code below to seed 10 users.
Then, run the command below to run the migration and generate seed data.
Define a Dto class
Let's create a simple UserDTO
class for a better understanding of DTOs. Inside your app directory, create a new directory named Dto and in that directory create a file named UserDTO.php file. Then, update the file with the following code.
Let's take a closer look at the methods in the class above.
Properties: The public properties (
name
,email
, andpassword
) are the data we want to transfer. Making them public allows easy access. You could make them private/protected and use getters if encapsulation is a concern. Each of the data is of type string. This assignment ensures that only requests with the correct data type will be saved to the databaseConstructor: The
__construct()
method initializes the DTO with data. When you create an instance ofUserDTO
, you'll provide the values forname
,email
, andpassword
.A static creation method: The
fromArray()
static method is an optional convenience method that allows you to create an instance of the DTO from an associative array. This can be particularly useful when working with response data or array data fetched from a database. A good example of when to use this would be when working with the$request
object in Laravel forms.
Usage example
Let's put our UserDTO
class to use in the app/Http/Controllers/UserController.php file. We will explore the two different ways that we can use our UserDTO class.
First, create the UserController.php file using the command below.
Then, head over to app/Http/Controllers/UserController.php and modify the contents with the code below.
In the code above, we created a new user using the UserDTO
class and some fake details using Faker. We instantiated a new UserDTO class and passed along the data needed to create a new user. It ensured the data passed along was in the correct type (strings in this case). We then passed the UserDTO
class return value to the User::create()
method to create a new user.
Alternatively, we can achieve the same result using the fromArray
static method of the UserDTO
class by the following.
To test this, add this route to your routes/web.php file like so.
Now, you can test that it works using the route /test/dto
in your browser of choice. The image below is the result of my test.
Use cases of DTOs in Laravel
There are different use cases for DTOs in Laravel development. You can use them in the following forms:
In API development for structuring responses
Form request validation and data transfer
Complex data structures for jobs or events
Service classes
Let's take a closer look at each of these use cases with examples.
API responses
One common use case for DTOs in Laravel is formatting API responses. This is particularly useful when you want to ensure a consistent structure across your API endpoints, or when you need to include additional computed properties that aren't directly stored in your database.
The code below shows an example of this.
Then, you can use this DTO in your controller to structure the API response as follows.
Form requests validation and data transfer
DTOs can be particularly useful when working with form requests. You can use a DTO to encapsulate the data from a request, ensuring that only valid, sanitized data is passed through your application's layers.
First, validate your request as usual in a FormRequest
class or directly in the controller, then instantiate a DTO with the validated data, as in the following example.
Complex data structures for jobs or events
DTOs shine when you need to work with complex data structures — especially when dispatching jobs or events that require specific data, as in the following example:
In the job's constructor, you can type-hint the DTO, ensuring that the job receives exactly what it needs, as in this example.
Service classes
When using service classes to handle business logic, DTOs can be used to transfer data from controllers to these services, ensuring that the services have all the data they need in a structured and type-safe manner.
Then, in the controller:
How to use a DTO in a Laravel project
Let's take a look at a second practical example of using DTOs in a Laravel project. We created a Laravel project earlier. This example will walk you through a simple CRUD layer for a blog app, seeding data, and integrating DTOs into the blog controller.
Create a migration and model for blog posts
To get started with the second DTO example implementation, navigate to the dto-project directory using the command below
Then, run the following Artisan command to create a migration and a model for your blog posts:
Open the newly generated migration file. It's in the database/migrations directory and ends with _create_posts_table.php. Add fields for your blog posts by updating the up()
function to match the definition below:
We also need to make the model's database fields mass-assignable. Modify the content of the generated model file, app/Models/Post.php, updating it to match the following:
Then, run the migration:
Seed the database with data
The next thing to do is to create a Seeder for the Post model using the command below:
Open the generated seeder file (database/seeders/PostsTableSeeder.php) and add some dummy data, by updating the run()
function as follows:
The next thing to do is to run the database migrations and then run our seeder:
Create a DTO for blog posts
We created a Dto folder inside the app directory in the first example. Navigate to that directory and create a PostDTO.php file inside it. The file path should be app/Dto/PostDTO.php. Replace the content of the file with the following.
Create CRUD operations in the blog controller using the DTO
Let's create a controller for the blog posts using the command below.
Now, open app/Http/Controllers/PostController.php and use the PostDTO
class in CRUD operations, by updating the controller to match the code below. For brevity, let's focus on the store()
and update()
methods as examples:
We used our PostDTO
class here to ensure data saved to the database are of the required type. We validated the data using the Validator::make()
method and passed the validated data to the PostDTO
class by calling the findOrFail()
static method of the PostDTO
.
Create a route
Our application needs a route. Let's create a simple API route to test. Open route/api.php and add the following to it.
Then, add the following use statement to the top of the file.
Test the application
Now, we should be able to test the API we just created. For this tutorial, I will test using Postman and Ngrok.
The first step in our test would be to serve the application locally. Let's start our Laravel development server using the command below.
This starts a server for development at http://127.0.0.1:8000. If port 8000 is in use, Laravel will choose a different port automatically. Our testing URL would be http://127.0.0.1:8000/api/create-post.
Open Postman and create a new request. Set the URL to http://127.0.0.1:8000/api/create-post and the request type to POST. Then, click Body > form-data. There, add two keys: title and content. Set title's value to "first post" and content's value to "first post body". Then, click Send.
The image below shows the result of a successful request.
Conclusion
DTOs offer a powerful paradigm for data handling in Laravel applications, from simplifying controller logic to enhancing API development. Following the practices outlined in this tutorial can improve your Laravel projects' structure, maintainability, and performance.
Happy coding!
Moses Anumadu is a software developer and online educator who loves to write clean, maintainable code. Offering content as a service. You can find him here.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.