{"id":283,"date":"2021-06-21T10:24:00","date_gmt":"2021-06-21T10:24:00","guid":{"rendered":"https:\/\/www.escueladefrikis.com\/?p=283"},"modified":"2021-06-21T10:24:00","modified_gmt":"2021-06-21T10:24:00","slug":"dto-en-laravel-8","status":"publish","type":"post","link":"https:\/\/escueladefrikis.com\/index.php\/2021\/06\/21\/dto-en-laravel-8\/","title":{"rendered":"Modelos DTO en Laravel 8"},"content":{"rendered":"\n<p>Los DTO o Data Transfer Object son los tipos de objetos utilizados para modelar la comunicaci\u00f3n entre sistemas, lo que en una API REST significa que son los modelos de las peticiones (requests) y respuestas (responses). En la implementaci\u00f3n de la API en Laravel, los controladores reciben un objeto gen\u00e9rico Request:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/**\n     * Store a newly created resource in storage.\n     *\n     * @param  \\Illuminate\\Http\\Request  $request\n     * @return \\Illuminate\\Http\\Response\n     *\/\n    public function store(Request $request)\n    {\n        \/\/ your code here! \n    }<\/code><\/pre>\n\n\n\n<p>Lo que implica que toda la validaci\u00f3n del modelo de datos se debe implementar en el controlador. Buscando referencias y librer\u00edas, encontr\u00e9 esta librer\u00eda para Data Transfer Objects (DTOs): <a href=\"https:\/\/github.com\/spatie\/data-transfer-object\">https:\/\/github.com\/spatie\/data-transfer-object<\/a> y vamos a ver c\u00f3mo aplicarla para modelar la petici\u00f3n o request.<\/p>\n<p><!--more--><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Soluci\u00f3n cl\u00e1sica: Request gen\u00e9rica<\/h3>\n\n\n\n<p>Vamos a ver un poco m\u00e1s en detalle la soluci\u00f3n cl\u00e1sica. Como indicaba al comienzo del art\u00edculo, el controlador recibe una Request gen\u00e9rica y por tanto es parte de la implementaci\u00f3n validar el DTO recibido. Por ejemplo, vamos a tener un modelo llamado \u00abBook\u00bb<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Book extends Model\n{\n    use HasFactory;\n\n    \/**\n     * The attributes that are mass assignable.\n     *\n     * @var array\n     *\/\n    protected $fillable = &#91;\n        'name',\n        'author',\n    ];\n}<\/code><\/pre>\n\n\n\n<p>Ahora vamos a implementar un endpoint que reciba un nuevo libro. En la implementaci\u00f3n en el controlador, validaremos la petici\u00f3n y se mostrar\u00e1, o bien los errores de la validaci\u00f3n en caso de que el modelo de la petici\u00f3n no sea correcto, o bien los datos recibidos:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/**\n     * Store a newly created resource in storage.\n     *\n     * @param  \\Illuminate\\Http\\Request  $request\n     * @return \\Illuminate\\Http\\Response\n     *\/\n    public function store(Request $request)\n    {\n        $validator = Validator::make($request-&gt;all(), &#91;\n            'name' =&gt;  'required',\n            'author' =&gt; 'required',\n        ]);\n \n        if($validator-&gt;fails()){\n            return response()-&gt;json($validator-&gt;errors(), 400); \n        }\n\n        $data=&#91;\n            'name' =&gt; $request&#91;'name'],\n            'author' =&gt; $request&#91;'author']\n        ];\n\n        return response()-&gt;json($data, 200); \n\n    }<\/code><\/pre>\n\n\n\n<p>Es un endpoint muy simple, pero la mitad del c\u00f3digo es para validar la petici\u00f3n, es decir, para validar un DTO.<\/p>\n<p>Vamos a ver ahora una soluci\u00f3n m\u00e1s optimizada utilizando la librer\u00eda <a href=\"https:\/\/github.com\/spatie\/data-transfer-object\">https:\/\/github.com\/spatie\/data-transfer-object<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Implementar una DTO de petici\u00f3n (request) en Laravel<\/h3>\n\n\n\n<p>Utilizando la librer\u00eda de DTO (<a href=\"https:\/\/github.com\/spatie\/data-transfer-object\">https:\/\/github.com\/spatie\/data-transfer-object<\/a>), vamos a definir una clase que gestione la petici\u00f3n (request) y adem\u00e1s se encargue de validar el modelo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nnamespace App\\Models\\Requests;\n\nuse Spatie\\DataTransferObject\\DataTransferObject;\nuse Illuminate\\Http\\Request;\nuse Validator;\n\nclass BookStoreRequest extends DataTransferObject\n{\n\n    public string $name;\n\n    public string $author;\n\n\n    public static function fromRequest(Request $request): self\n    {\n        return BookStoreRequest::fromArray($request-&gt;all());\n    }\n\n    public static function fromArray($request): self\n    {\n\n        $validator = Validator::make($request, &#91;\n            'name' =&gt;  'required',\n            'author' =&gt;  'required',\n        ]);\n \n        if($validator-&gt;fails()){\n            return response()-&gt;json($validator-&gt;errors(), 400); \n        }\n\n        return new self(&#91;\n            'name' =&gt; $request&#91;'name'],\n            'author' =&gt; $request&#91;'author'],\n        ]);\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>La clase que modela el DTO de la request dispone de dos m\u00e9todos est\u00e1ticos que devuelven un objeto de la clase, una parte de la Request gen\u00e9rica de Laravel (\u00abfromRequest\u00bb) y una segunda que tiene como entrada un array (\u00abfromArray\u00bb).<\/p>\n<p>Ahora podemos modificar el controller, que quedar\u00eda:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>use App\\Models\\Requests\\BookStoreRequest;\n\n    \/**\n     * Store a newly created resource in storage.\n     *\n     * @param  \\Illuminate\\Http\\Request  $request\n     * @return \\Illuminate\\Http\\Response\n     *\/\n    public function store(Request $request)\n    {\n        $bookStoreRequest = BookStoreRequest::fromRequest($request);\n\n        $data=&#91;\n            'name' =&gt; $request&#91;'name'],\n            'author' =&gt; $request&#91;'author']\n        ];\n\n        return response()-&gt;json($data, 200); \n\n    }<\/code><\/pre>\n\n\n\n<p>De esta manera, la clase BookStoreRequest se encarga de modelar y validar el DTO de la petici\u00f3n, dejando \u00fanicamente en el controlador la implementaci\u00f3n de la l\u00f3gica de negocio. En mi opini\u00f3n, una aproximaci\u00f3n mucho m\u00e1s adecuada.<\/p>\n<p>Pero a\u00fan podemos mejorarlo un poco m\u00e1s \ud83d\ude42<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Inyecci\u00f3n del DTO como Request<\/h3>\n\n\n\n<p>Ahora vamos a sustituir el tipo de objeto recibido por el controlador, cambiando la Request gen\u00e9rica:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    public function store(Request $request)\n    {\n        \/\/ your code here!\n    }<\/code><\/pre>\n\n\n\n<p>y sustituirla por el DTO que hemos implementado:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    public function store(BookStoreRequest $request)\n    {\n        \/\/ your code here!\n    }<\/code><\/pre>\n\n\n\n<p>En este caso, cuando Laravel intenta inyectar \u00abBookStoreRequest \u00ab, intenta crear la instancia del objeto a partir de la \u00abRequest\u00bb. Por tanto, para ello tenemos que a\u00f1adir un constructor a la clase \u00abBookStoreRequest\u00bb que tenga como argumento de entrada la \u00abRequest\u00bb e incluya la \u00b4l\u00f3gica de validaci\u00f3n:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nnamespace App\\Models\\Requests;\n\nuse Spatie\\DataTransferObject\\DataTransferObject;\nuse Illuminate\\Http\\Request;\nuse Validator;\n\nclass BookStoreRequest extends DataTransferObject\n{\n\n    public string $name;\n\n    public string $author;\n\n\n    public function __construct(Request $request)\n    {\n        $validator = Validator::make($request, &#91;\n            'name' =&gt;  'required',\n            'author' =&gt;  'required',\n        ]);\n \n        if($validator-&gt;fails()){\n            return response()-&gt;json($validator-&gt;errors(), 400); \n        }\n\n        \/\/ everything OK, we set the attributes\n        $this-&gt;name = $request&#91;'name'];\n        $this-&gt;author = $request&#91;'author];\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>Por tanto, ya podemos hacer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>use App\\Models\\Requests\\BookStoreRequest;\n\n   \/**\n     * Store a newly created resource in storage.\n     *\n     * @param  App\\Models\\Requests\\BookStoreRequest  $request\n     * @return \\Illuminate\\Http\\Response\n     *\/\n    public function store(BookStoreRequest $request)\n    {\n\n        $data=&#91;\n            'name' =&gt; $request-&gt;name,\n            'author' =&gt; $request-&gt;author\n        ];\n\n        return response()-&gt;json($data, 200); \n\n    }<\/code><\/pre>\n\n\n\n<p>E incre\u00edblemente, esto&#8230; <strong>funciona<\/strong>!<\/p>\n\n\n\n<div class=\"wp-block-cover is-light\"><span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background has-background-dim\"><\/span><img loading=\"lazy\" decoding=\"async\" width=\"671\" height=\"561\" class=\"wp-block-cover__image-background wp-image-284\" alt=\"\" src=\"https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-1.png\" data-object-fit=\"cover\" srcset=\"https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-1.png 671w, https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-1-300x251.png 300w\" sizes=\"auto, (max-width: 671px) 100vw, 671px\" \/><div class=\"wp-block-cover__inner-container is-layout-flow wp-block-cover-is-layout-flow\">\n<p class=\"has-large-font-size wp-block-paragraph\">DTO Request en Laravel 8<\/p>\n\n\n<\/div><\/div>\n\n\n\n<p>Como puedes ver, el c\u00f3digo del controlador queda muy limpio y \u00fanicamente tiene l\u00f3gica de negocio.<\/p>\n<p>Aun as\u00ed, la soluci\u00f3n todav\u00eda no est\u00e1 completa, ya que si bien es cierto que valida la entrada, en caso que la petici\u00f3n no sea correcta obtenemos el siguiente error:<\/p>\n\n\n\n<div class=\"wp-block-cover is-light\"><span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background has-background-dim\"><\/span><img loading=\"lazy\" decoding=\"async\" width=\"888\" height=\"656\" class=\"wp-block-cover__image-background wp-image-285\" alt=\"\" src=\"https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-error-1.png\" data-object-fit=\"cover\" srcset=\"https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-error-1.png 888w, https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-error-1-300x222.png 300w, https:\/\/escueladefrikis.com\/wp-content\/uploads\/2021\/05\/postman-BookStoreRequest-error-1-768x567.png 768w\" sizes=\"auto, (max-width: 888px) 100vw, 888px\" \/><div class=\"wp-block-cover__inner-container is-layout-flow wp-block-cover-is-layout-flow\">\n<p class=\"has-large-font-size wp-block-paragraph\">DTO Request excepci\u00f3n<\/p>\n<\/div><\/div>\n\n\n\n<p>Este error nos indica que est\u00e1 esperando un objeto de tipo \u00abBookStoreRequest\u00bb y le estamos devolviendo un \u00abJsonResponse\u00bb, que es la salida del validador:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class BookStoreRequest extends DataTransferObject\n{\n\n...\n    public function __construct(Request $request)\n    {\n\n.. \n        if($validator-&gt;fails()){\n            <span style=\"color:#ff6663\" class=\"tadv-color\">return response()-&gt;json($validator-&gt;errors(), 400);<\/span> \n        }\n\n...\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>En este punto, lo realmente interesante ser\u00eda que no llegara a ejecutarse nada en el controlador sino que simplemente se devolviera una excepci\u00f3n, y para ello necesitamos a\u00f1adir una nueva funcionalidad a la API: <a href=\"https:\/\/www.escueladefrikis.com\/2021\/08\/23\/exception-handler-en-laravel-8\/\">la gesti\u00f3n de excepciones o Exception Handling en Laravel 8<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,5],"tags":[22,25],"class_list":["post-283","post","type-post","status-publish","format-standard","hentry","category-laravel-8","category-programacion","tag-laravel","tag-php"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/posts\/283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/comments?post=283"}],"version-history":[{"count":0,"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/posts\/283\/revisions"}],"wp:attachment":[{"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/media?parent=283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/categories?post=283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/escueladefrikis.com\/index.php\/wp-json\/wp\/v2\/tags?post=283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}