Clean up php code with DTO

This is my first article, so get ready to catch stones.





When writing a new method or service, we try to abstract it as much as possible from external dependencies, so that the new functionality implements only the logic laid down for it. In fact, this is what one of the SOLID principles tells us - the single responsibility principle .





I constantly come across a code where if a method has more than two input arguments, a conditional (array $ args) is added, which entails the implementation of a check for the presence of a key, or it is absent, and then the likelihood that the method can be painted over at runtime increases.





Perhaps, such an approach in PHP has developed historically, due to the lack of strong typing and such OOP. After all, as for me, only from version 7 it was possible to more or less implement typing + OOP, using strict_types and type hinting.





Also, the call of such methods can be accompanied by a description of the array that we will pass. Or some kind of array with garbage is passed at all, and the method simply takes the keys it needs. For example, a service for creating a user:





$userService->create([         
    'name' => $object->name,         
    'phone' => $object->phone,         
    'email' => $object->email,     
]);
      
      



, DTO’. DTO , , . . , , , .





DTO, , . . , , , , .





, .





ClassTransformer

. . , . Laravel :





class UserController extends Controller {
	public function __construct(
      private UserService $userService,
	) {}

	public function createUser(CreateUserRequest $request)
	{
      $dto = ClassTransformer::transform(CreateUserDTO::class, $request);
      $user = $this->userService->create($dto);
      return response(UserResources::make($user));
	}
}
      
      



class CreateUserDTO
{
    public string $name;
    public string $email;
    public string $phone;
}
      
      



: name, phone email. , , , . . transform , object, .  





. , DTO, :





class CreateUserDTO
{
    public string $name;
    public string $email;
    public string $phone;
    
    public static function transform(mixed $args):CreateUserDTO
    {
        $dto = new self();
        $dto->name = $args['fullName'];
        $dto->email = $args['mail'];
        $dto->phone = $args['phone'];
        return $dto;
    }
}
      
      



, , . ? , PHPDoc . , :





class PurchaseDTO
{
    /** @var array<\DTO\ProductDTO> $products Product list */
    public array $products;
    
    /** @var \DTO\UserDTO $user */
    public UserDTO $user;
}
      
      



, . .





, .. . alias , .





?









  • ,









  • ,





  • IDE .





, . Spatie - https://github.com/spatie/data-transfer-object





DTO, DTO, , . , new DTO() .





, , NestJS - plainToClass. , , . ORM ( ), :)





Roadmap

  • Implement the afterTransform method that will be called after the DTO is initialized. This will allow you to more flexibly customize the casting to the class. At the moment, if the input keys are different from the internal DTOs, you need to describe the transform method yourself. And if out of 20 parameters only one has a different key, we will have to describe the conversion of all 20. And with the afterTransform method, we can customize the conversion of only the parameter we need, and all the rest will be processed by the package.





  • PHP 8 Attribute Support





That's all.








All Articles