From f235f729572962b0a49ac99e72789f673568d091 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 10 Oct 2023 12:27:10 +0100 Subject: [PATCH] Add new Cast attribute --- src/Attributes/Cast.php | 23 +++++++++++++++++++++ src/SimpleDTO.php | 31 +++++++++++++++++++++++++--- src/ValidatedDTO.php | 2 +- tests/Datasets/UserAttributesDTO.php | 14 +++++++++++++ tests/Unit/AttributesTest.php | 30 +++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 src/Attributes/Cast.php diff --git a/src/Attributes/Cast.php b/src/Attributes/Cast.php new file mode 100644 index 0000000..49b7585 --- /dev/null +++ b/src/Attributes/Cast.php @@ -0,0 +1,23 @@ +validatedData = $this->validatedData(); /** @var array $casts */ - $casts = $this->casts(); + $casts = $this->buildCasts(); foreach ($this->validatedData as $key => $value) { $this->{$key} = $value; @@ -161,7 +164,7 @@ protected function passedValidation(): void foreach ($defaults as $key => $value) { if ( ! property_exists($this, $key) || - empty($this->{$key}) + ! isset($this->{$key}) ) { if (! array_key_exists($key, $casts)) { if ($this->requireCasting) { @@ -205,7 +208,7 @@ protected function validatedData(): array $result = []; /** @var array $casts */ - $casts = $this->casts(); + $casts = $this->buildCasts(); foreach ($this->data as $key => $value) { if (in_array($key, $acceptedKeys)) { @@ -254,6 +257,21 @@ protected function shouldReturnNull(string $key, mixed $value): bool return is_null($value); } + protected function buildCasts(): array + { + $casts = []; + foreach ($this->dtoCasts as $property => $cast) { + $casts[$property] = is_null($cast->param) + ? new $cast->type() + : new $cast->type(new $cast->param()); + } + + return [ + ...$this->casts(), + ...$casts, + ]; + } + private function buildAttributesData(): void { $publicProperties = $this->getPublicProperties(); @@ -276,6 +294,12 @@ private function buildAttributesData(): void $this->dtoDefaults[$property] = $attributeInstance->value; } + $castProperties = $this->getPropertiesForAttribute($publicProperties, Cast::class); + foreach ($castProperties as $property => $attribute) { + $attributeInstance = $attribute->newInstance(); + $this->dtoCasts[$property] = $attributeInstance; + } + $mapDataProperties = $this->getPropertiesForAttribute($publicProperties, Map::class); foreach ($mapDataProperties as $property => $attribute) { $attributeInstance = $attribute->newInstance(); @@ -471,6 +495,7 @@ private function isforbiddenProperty(string $property): bool 'dtoRules', 'dtoMessages', 'dtoDefaults', + 'dtoCasts', 'dtoMapData', 'dtoMapTransform', ]); diff --git a/src/ValidatedDTO.php b/src/ValidatedDTO.php index 6e44ff6..eb70a93 100644 --- a/src/ValidatedDTO.php +++ b/src/ValidatedDTO.php @@ -44,7 +44,7 @@ protected function validatedData(): array $result = []; /** @var array $casts */ - $casts = $this->casts(); + $casts = $this->buildCasts(); foreach ($this->data as $key => $value) { if (in_array($key, $acceptedKeys)) { diff --git a/tests/Datasets/UserAttributesDTO.php b/tests/Datasets/UserAttributesDTO.php index 7963245..bb33251 100644 --- a/tests/Datasets/UserAttributesDTO.php +++ b/tests/Datasets/UserAttributesDTO.php @@ -4,9 +4,14 @@ namespace WendellAdriel\ValidatedDTO\Tests\Datasets; +use WendellAdriel\ValidatedDTO\Attributes\Cast; use WendellAdriel\ValidatedDTO\Attributes\DefaultValue; use WendellAdriel\ValidatedDTO\Attributes\Map; use WendellAdriel\ValidatedDTO\Attributes\Rules; +use WendellAdriel\ValidatedDTO\Casting\ArrayCast; +use WendellAdriel\ValidatedDTO\Casting\BooleanCast; +use WendellAdriel\ValidatedDTO\Casting\FloatCast; +use WendellAdriel\ValidatedDTO\Casting\IntegerCast; use WendellAdriel\ValidatedDTO\Concerns\EmptyCasts; use WendellAdriel\ValidatedDTO\Concerns\EmptyDefaults; use WendellAdriel\ValidatedDTO\Concerns\EmptyRules; @@ -25,5 +30,14 @@ class UserAttributesDTO extends ValidatedDTO #[Rules(['sometimes', 'boolean'])] #[DefaultValue(true)] + #[Cast(BooleanCast::class)] public bool $active; + + #[Rules(['sometimes', 'integer'])] + #[Cast(IntegerCast::class)] + public ?int $age; + + #[Rules(['sometimes', 'array'])] + #[Cast(type: ArrayCast::class, param: FloatCast::class)] + public ?array $grades; } diff --git a/tests/Unit/AttributesTest.php b/tests/Unit/AttributesTest.php index 29602ed..4a9cabf 100644 --- a/tests/Unit/AttributesTest.php +++ b/tests/Unit/AttributesTest.php @@ -53,3 +53,33 @@ 'active' => true, ]); }); + +it('casts the DTO data using the Cast attribute', function () { + $userDTO = new UserAttributesDTO([ + 'name' => $this->subject_name, + 'email' => $this->subject_email, + 'active' => '0', + 'age' => '25', + 'grades' => ['10', '9.5', '8.5'], + ]); + + expect($userDTO)->toBeInstanceOf(UserAttributesDTO::class) + ->and($userDTO->validatedData) + ->toBe([ + 'name' => $this->subject_name, + 'email' => $this->subject_email, + 'active' => false, + 'age' => 25, + 'grades' => [10.0, 9.5, 8.5], + ]) + ->and($userDTO->validator->passes()) + ->toBeTrue() + ->and($userDTO->toArray()) + ->toBe([ + 'full_name' => $this->subject_name, + 'email' => $this->subject_email, + 'active' => false, + 'age' => 25, + 'grades' => [10.0, 9.5, 8.5], + ]); +});