• Stars
    star
    55
  • Rank 537,381 (Top 11 %)
  • Language
    PHP
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Data Transfer Object (DTO) for Laravel

Laravel DTO

Author PHP Version Laravel Version Octane Compatibility Build Status Coverage Status Quality Score Latest Version Software License PSR-12 Total Downloads

Laravel DTO integrates DTO, a package inspired by Lachlan Krautz' excellent data-transfer-object, with the functionalities of Laravel.

A data transfer object (DTO) is an object that carries data between processes. DTO does not have any behaviour except for storage, retrieval, serialization and deserialization of its own data. DTOs are simple objects that should not contain any business logic but rather be used for transferring data.

Below are explained the advantages brought by this package in a Laravel application. In order to discover all the features of DTO, please refer to the full DTO documentation.

Install

Via Composer:

composer require cerbero/laravel-dto

To customize some aspects of this package, the config/dto.php file can optionally be generated via:

php artisan vendor:publish --tag=dto

Usage

Generate DTOs

DTOs for Eloquent models can be automatically generated by running the following Artisan command:

php artisan make:dto App/User

The database table of the specified model is scanned to populate the DTO properties. Furthermore, if the model has relationships, a DTO is also generated for each related model. For example, if our User model looks like:

class User extends Model
{
    public function posts()
    {
        return $this->hasMany('App\Post');
    }
}

The DTOs App\Dtos\UserData and App\Dtos\PostData are generated like so:

use Cerbero\LaravelDto\Dto;
use Carbon\Carbon;

use const Cerbero\Dto\PARTIAL;
use const Cerbero\Dto\IGNORE_UNKNOWN_PROPERTIES;

/**
 * The data transfer object for the User model.
 *
 * @property int $id
 * @property string $name
 * @property Carbon $createdAt
 * @property Carbon $updatedAt
 * @property PostData[] $posts
 */
class UserData extends Dto
{
    /**
     * The default flags.
     *
     * @var int
     */
    protected static $defaultFlags = PARTIAL | IGNORE_UNKNOWN_PROPERTIES;
}

/**
 * The data transfer object for the Post model.
 *
 * @property int $id
 * @property string $content
 * @property int $userId
 * @property Carbon $createdAt
 * @property Carbon $updatedAt
 * @property UserData $user
 */
class PostData extends Dto
{
    /**
     * The default flags.
     *
     * @var int
     */
    protected static $defaultFlags = PARTIAL | IGNORE_UNKNOWN_PROPERTIES;
}

By default, DTOs are generated in the Dtos directory which is created where models are. For example the DTO for App\User is generated as App\Dtos\UserData and the DTO for App\Users\User is generated as App\Users\Dtos\UserData.

To change either the location or the suffix Data of generated DTOs, we can create a DTO qualifier by implementing the interface DtoQualifierContract and replace the default qualifier in config/dto.php. The example below qualifies a DTO in the directory of the model and adds the suffix Dto:

use Cerbero\LaravelDto\DtoQualifierContract;

class MyDtoQualifier implements DtoQualifierContract
{
    public function qualify(string $model): string
    {
        return $model . 'Dto';
    }
}

// in config/dto.php
return [
    'qualifier' => MyDtoQualifier::class,
];

Finally, if a model has already its own DTO generated, we can overwrite it with the option --force or -f:

php artisan make:dto App/User --force

Instantiate a DTO

In addition to the traditional ways to instantiate a DTO, Laravel DTO provides handy methods to create a new instance of DTO from HTTP requests, Eloquent models or other common interfaces present in Laravel.

For example UserData can be instantiated from an HTTP request by calling the method fromRequest():

use App\Dtos\UserData;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function store(Request $request)
    {
        $dto = UserData::fromRequest($request);
    }
}

The request passed to the method fromRequest() is optional: if not provided, the current application request is used to instantiate UserData.

By default the flags PARTIAL and IGNORE_UNKNOWN_PROPERTIES are applied to the DTO when it is instantiated from a request. Additional flags can be passed as second parameter to further customize the behaviour of the DTO.


To instantiate a DTO from an Eloquent model, we can call the method fromModel():

$user = new User(['name' => 'Phil']);

$dto = UserData::fromModel($user);

The flags PARTIAL, IGNORE_UNKNOWN_PROPERTIES and CAST_PRIMITIVES are applied to the DTO when it is instantiated from a model. Additional flags can be passed as second parameter.


Finally, the method from() instantiates a DTO from several interfaces (specific to Laravel or not), including:

  • Illuminate\Support\Enumerable
  • Illuminate\Contracts\Support\Arrayable
  • Illuminate\Contracts\Support\Jsonable
  • JsonSerializable
  • Traversable
  • any value that can be casted into an array

In this case no flags are applied by default, but they can still be passed as second parameter.

Resolve a DTO

As long as PARTIAL is set in the default flags of a DTO, such DTO can be automatically resolved by the Laravel IoC container with the data carried by the current application request:

use App\Dtos\UserData;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    public function store(UserData $dto)
    {
        // ...
    }
}

Convert into DTO

Another way to get an instance of DTO from different objects is letting them use the trait TurnsIntoDto and call the method toDto():

use Cerbero\LaravelDto\Traits\TurnsIntoDto;

class StoreUserRequest extends Request
{
    use TurnsIntoDto;
}

class User extends Model
{
    use TurnsIntoDto;

    protected $dtoClass = UserData::class;
}

class Example
{
    use TurnsIntoDto;

    protected function getDtoClass(): ?string
    {
        return $condition ? UserData::class : OtherDto::class;
    }
}

$dto = $request->toDto(UserData::class, MUTABLE);
$dto = $user->toDto(CAST_PRIMITIVES);
$dto = $example->toDto();

Classes using the trait can specify the DTO to turn into by:

  • passing the DTO class name as first parameter of the method toDto()
  • defining the property $dtoClass
  • overriding the method getDtoClass() if custom logic is needed

Flags can optionally be passed as second parameter, or first parameter if the DTO class is already defined in the class using the trait. When models turn into DTOs, the flag CAST_PRIMITIVES is added to help casting values if casts are not defined on the Eloquent models.

Convert into array

By default Laravel DTO registers a value converter for Carbon instances. When a DTO is converted into array, all its Carbon objects are turned into an atom string and then converted back into Carbon instances when a new DTO is instantiated:

$dto = UserData::make(['created_at' => '2000-01-01']);
$dto->createdAt; // Carbon instance
$data = $dto->toArray(); // ['created_at' => '2000-01-01T00:00:00+00:00']

$dto = UserData::make($data);
$dto->createdAt; // Carbon instance

Conversions can be added or removed in the config/dto.php file, specifically via the key conversions:

use Carbon\Carbon;
use Cerbero\LaravelDto\Manipulators\CarbonConverter;

return [
    'conversions' => [
        Carbon::class => CarbonConverter::class,
    ],
];

Listen to events

The only feature added by this package to listeners is the ability to resolve dependencies via the Laravel IoC container. Dependencies can be injected into listeners constructor to be automatically resolved.

Listeners can be added or removed in the config/dto.php file, specifically via the key listeners:

return [
    'listeners' => [
        UserData::class => UserDataListener::class,
    ],
];

Define flags globally

Sometimes we may want all our DTOs to share the same flags, an example might be the need to always work with mutable DTOs. An easy way to accomplish that is defining such flags in the config/dto.php file:

return [
    'flags' => MUTABLE,
];

Support for macros

In case we need to add functionalities to all DTOs, an option might be using macros. Please refer to the Laravel documentation to see an example of how to register a macro.

DTO debugging

When using the helpers dump() or dd(), only DTOs data will be shown instead of all the underlying architecture that makes the package work:

dd($dto);

// only DTO data is shown:

App\Dtos\UserData {#3224
  +name: "Phil"
}

Change log

Please see CHANGELOG for more information on what has changed recently.

Testing

composer test

Contributing

Please see CONTRIBUTING and CODE_OF_CONDUCT for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

More Repositories

1

json-parser

🧩 Zero-dependencies lazy parser to read JSON of any dimension and from any source in a memory-efficient way.
PHP
676
star
2

lazy-json

🐼 Framework-agnostic package to load JSON of any dimension and from any source into Laravel lazy collections recursively.
PHP
235
star
3

enum

🎲 Zero-dependencies PHP library to supercharge enum functionalities.
PHP
188
star
4

command-validator

Validate Laravel console commands input.
PHP
160
star
5

laravel-enum

Discontinued. Enum generator for Laravel.
PHP
136
star
6

eloquent-inspector

πŸ•΅οΈ Inspect Laravel Eloquent models to collect properties, relationships and more.
PHP
115
star
7

Workflow

Laravel 5 package to create extendible and maintainable apps by harnessing the power of pipelines.
PHP
109
star
8

query-filters

Laravel package to filter Eloquent model records based on query parameters. Fully inspired by the Laracasts episode https://laracasts.com/series/eloquent-techniques/episodes/4
PHP
84
star
9

lazy-json-pages

πŸ“œ Framework-agnostic package to load items from any paginated JSON API into a Laravel lazy collection via async HTTP requests.
PHP
82
star
10

notifiable-exception

Laravel package to send notifications when some exceptions are thrown.
PHP
76
star
11

exception-handler

Extend the Laravel exception handler to let service providers determine how custom exceptions should be handled.
PHP
60
star
12

Auth

Laravel authentication module.
PHP
49
star
13

octane-testbench

β›½ Set of utilities to test Laravel applications powered by Octane.
PHP
42
star
14

sql-dumper

Laravel package to dump SQL queries, related EXPLAIN and location in code in different formats.
PHP
23
star
15

pest-plugin-laravel-octane

β›½ Pest plugin to test Laravel applications powered by Octane.
PHP
21
star
16

json-objects

Extract objects from large JSON files, endpoints or streams while saving memory.
PHP
20
star
17

dto

Data Transfer Object (DTO).
PHP
16
star
18

Transformer

Framework agnostic package to transform objects and arrays by manipulating, casting and mapping their properties.
PHP
14
star
19

Sublime-Text-PHP-and-Laravel-Snippets

Sublime Text snippets to ease development with PHP and Laravel.
13
star
20

console-tasker

🦾 Laravel package to create lean, powerful, idempotent and beautiful Artisan commands.
PHP
10
star
21

workflow-demo

Demo for Workflow repository
CSS
9
star
22

Date

Framework agnostic and easy to use tool to work with dates.
PHP
6
star
23

start

Mac service written in Automator to run several softwares and commands by pressing a hot key.
AppleScript
2
star
24

fluent-api

Framework agnostic starting point to perform fluent calls to any API.
PHP
1
star
25

Affiliate

PHP
1
star