DTOs e PHP: simplificando a transferência de dados entre as camadas da aplicação
Tags: php, dto
Published: 2024-07-09 12:44:00

## O padrão DTO

O DTO (Data Transfer Object) é um padrão de projeto que visa ter objetos usados exclusivamente para a transferência de dados entre camadas de uma aplicação. É um objeto anêmico, ou seja, a classe tem apenas atributos e sem métodos que manipulem dados, apenas de construção do objeto.

## Porque não um utilizar um array para receber dados em método?

Observe a classe `CreateProduct`:

```php
<?php

namespace App\Actions\Product;

use App\Product\Contracts\ProductRepository;

class CreateProduct
{
    public function __construct(private ProductRepository $repository) {}

    public function execute(array $data): Product
    {
        //
    }
}
```

Como o método `execute()` recebe um array associativo, quem instanciar essa classe precisará abrir o método e ver quais chaves do array será necessário informar.

### Substituindo o array pelo DTO

Podemos construir uma classe DTO no PHP utilizando o modificador `readonly` para que os atributos dos objetos não sejam alterados.

PHP 8.1:

```php
<?php

namespace App\Dto;

readonly class ProductDto
{
    public function __construct(
        public string $name,
        public string $description,
        public float $price,
        public int $quantity,
        public int $categoryId,
        public int $brandId,
        public string $sku,
        public string $ean
    ) {}
}
```

E na classe `CreateProduct` podemos alterar o método `execute()`:
```php
public function execute(ProductDto $data): Product
{
    //
}
```

### Benefícios para a IDE

Ao utilizar a classe DTO no lugar do array associativo a IDE irá ajudar a identificar os atributos necessários:

![Instanciando a classe ProductDto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/un8cxpafronxqy9rmoqs.png)

E também na utilização desses dados:


![Utilizando objeto ProductDto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3s12ihhkxfgumufau2c.png)


### Indo além com métodos estáticos

Para facilitar a criação desses objetos podemos criar métodos estáticos:

```php
public static function fromRequest(Request $request): self
{
    return new self(
        name: $request->name,
        description: $request->description,
        price: $request->price,
        quantity: $request->quantity,
        categoryId: $request->category_id,
        brandId: $request->brand_id,
        sku: $request->sku,
        ean: $request->ean
    );
}

public static function fromArray(array $data): self
{
    return new self(
        name: $data['name'],
        description: $data['description'],
        price: $data['price'],
        quantity: $data['quantity'],
        categoryId: $data['categoryId'],
        brandId: $data['brandId'],
        sku: $data['sku'],
        ean: $data['ean']
    );
}
```

E utilizando dessa forma:
```php
$data = ProductDto::fromRequest($request);
```

### Quando não utilizar

Não vejo a necessidade de utilizar um DTO como parâmetro em métodos públicos que já indicam o dado que espera receber, como por exemplo um método que buscará uma lista de produtos pelos IDs informados:

```php
class FindProducts
{
    public function execute(array $productsIds): Product
    {
        //
    }
}
```