From 6f06760a7f40221bfdefe75343f917bfe69d4ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Robles?= Date: Tue, 17 Oct 2023 17:23:46 +0200 Subject: [PATCH] add docs folder for gitbook --- docs/README.md | 38 ++++++ docs/typescript.md | 29 +++++ docs/usage.md | 290 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/typescript.md create mode 100644 docs/usage.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..1fe837b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,38 @@ +--- +description: Installing Laravel DTO in your application. +--- + +# Getting started + +Grab the dependency with Composer + +```bash +composer require open-southeners/laravel-dto +``` + +### Create new data transfer object + +To create a new DTO class run the following command on your project: + +```bash +php artisan make:dto CreatePostDataNow +``` + +You should have a file with a path like `app/DataTransferObjects/CreatePostData.php` which looks like this: + +```php + 'slug'], with: [ + Post::class => ['author', 'author.role'], + Film::class => 'reviews', + ])] + public Post|Film $taggable, + public string $taggableType, + ) { + // + } +} +``` + +This way we are binding a `taggable` entity that when is a post will be using slug on this property, while films will use their defaults (`id`). + +When loading a post will be getting its `author` and author's `role`, if otherwise is a film it will only load its reviews. + +### Mapping collections + +We determine as collections arrays and Laravel's collections because of some particular mapping process we do to them, lets imagine we send this to our backend: + +```json +{ + "tags": "1,3,91" +} +``` + +Using the following DTO: + +```php +final class CreatePostData extends DataTransferObject +{ + public function __construct( + public array $tags + ) { + // + } +} +``` + +We should get a array from this delimited list, now let's say we wanted to have integers, we could just use a docblock to help us. + +```php +final class CreatePostData extends DataTransferObject +{ + /** + * @param int[] $tags + */ + public function __construct( + public array $tags + ) { + // + } +} +``` + +Now we've an array of integers and so our IDE can also help us when using this property. **But we're not limited to only native types, we can also use models! Sending a comma-delimited list of IDs and typing this properly.** + +Same will go for **Laravel collections**, just typing it properly like so: + +```php +use Illuminate\Support\Collection; + +class CreatePostData extends DataTransferObject +{ + /** + * @param \Illuminate\Support\Collection<\App\Models\Tag> $tags + */ + public function __construct( + public Collection $tags + ) { + // + } +} +``` + +The example at the top will bind tag IDs or an array of IDs or tags model instances to a collection of tags instances. + +### Mapping with default values + +In case you want some default data mapped whenever you use `fromArray` you can use `withDefaults` like: + +```php +final class CreatePostData extends DataTransferObject +{ + public function __construct( + public string $title, + public PostStatus $status, + public string|null $description = null, + public array $tags = [] + ) { + // + } + + /** + * Add default data to data transfer object. + */ + public function withDefaults(): void + { + // Filled will check wether description is on the request or property not null (depending on the context) + if (! $this->filled('description')) { + $this->description = 'Example of a description...'; + } + + if (empty($this->tags)) { + $this->tags = ['generic', 'post']; + } + } +} +``` + +#### Default value using attribute + +You can use PHP attributes instead, which simplifies it even more as you **only need to send the raw value without it being mapped**: + +```php +use OpenSoutheners\LaravelDto\Attributes\WithDefaultValue; + +final class CreatePostData extends DataTransferObject +{ + public function __construct( + public string $title, + public PostStatus $status, + #[WithDefaultValue('Example of a description...')] + public string|null $description = null, + #[WithDefaultValue(['generic', 'post'])] + public array $tags = [] + ) { + // + } +} +``` + +## Usage in controllers + +Now at the controller level you may do something like the following: + +```php +// PostController.php + +public function store(CreatePostFormRequest $request) +{ + $post = $this->repository->create( + CreatePostData::fromRequest($request) + ); + + // Response here... +} +``` + +Or in case you're outside of a request context (HTTP call) you can use `fromArray`: + +```php +// CreatePostCommand.php + +$data = CreatePostData::fromArray([ + 'title' => 'Hello world', + 'status' => PostStatus::Published->value, + 'tags' => 'hello,world' +]); + +$data->title; // Hello world +``` + +### Controller binding resolution + +::: tip +This way the data transfer object can also get route binding stuff like models. Check this whole section for further understanding on this feature. +::: + +You can also save code by directly typing an parameter on your controller as a DTO class. + +To do so, you might need to create a DTO with the `ValidatedDataTransferObject` interface, for that you have the command: + +```bash +php artisan make:dto PostCreateData --request +``` + +::: tip +It can also convert validation rules from a `FormRequest` file to a DTO by sending the class path to the `--request` option. +::: + +Then you need to fill the `request` method with the class string (full qualified class path): + +```php +