From ca430e6fa19ee703a4238cc8fe6f5aee91f57e7d Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Mon, 4 Sep 2023 16:25:36 +0100 Subject: [PATCH] Add ability to cast array items with ArrayCast --- README.md | 14 ++++++++++++++ src/Casting/ArrayCast.php | 10 +++++++++- tests/Unit/ArrayCastTest.php | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b74a866..f78590d 100644 --- a/README.md +++ b/README.md @@ -667,6 +667,20 @@ protected function casts(): array } ``` +If you want to cast all the elements inside the array, you can pass a `Castable` to the `ArrayCast` +constructor. Let's say that you want to convert all the items inside the array into integers: + +```php +protected function casts(): array +{ + return [ + 'property' => new ArrayCast(new IntegerCast()), + ]; +} +``` + +This works with all `Castable`, including `DTOCast` and `ModelCast` for nested data. + ### Boolean For string values, this uses the `filter_var` function with the `FILTER_VALIDATE_BOOLEAN` flag. diff --git a/src/Casting/ArrayCast.php b/src/Casting/ArrayCast.php index 85bbb43..44f9dbb 100644 --- a/src/Casting/ArrayCast.php +++ b/src/Casting/ArrayCast.php @@ -6,6 +6,10 @@ final class ArrayCast implements Castable { + public function __construct(private ?Castable $type = null) + { + } + public function cast(string $property, mixed $value): array { if (is_string($value)) { @@ -14,6 +18,10 @@ public function cast(string $property, mixed $value): array return is_array($jsonDecoded) ? $jsonDecoded : [$value]; } - return is_array($value) ? $value : [$value]; + $result = is_array($value) ? $value : [$value]; + + return blank($this->type) + ? $result + : array_map(fn ($item) => $this->type->cast($property, $item), $result); } } diff --git a/tests/Unit/ArrayCastTest.php b/tests/Unit/ArrayCastTest.php index 6f2b2f4..98762e0 100644 --- a/tests/Unit/ArrayCastTest.php +++ b/tests/Unit/ArrayCastTest.php @@ -3,6 +3,10 @@ declare(strict_types=1); use WendellAdriel\ValidatedDTO\Casting\ArrayCast; +use WendellAdriel\ValidatedDTO\Casting\BooleanCast; +use WendellAdriel\ValidatedDTO\Casting\DTOCast; +use WendellAdriel\ValidatedDTO\Casting\IntegerCast; +use WendellAdriel\ValidatedDTO\Tests\Datasets\ValidatedDTOInstance; it('properly casts from json string to array') ->expect(fn () => new ArrayCast()) @@ -23,3 +27,32 @@ ->expect(fn () => new ArrayCast()) ->cast(test_property(), ['a', 'A', 1]) ->toBe(['a', 'A', 1]); + +it('properly casts a BooleanCast to array') + ->expect(fn () => new ArrayCast(new BooleanCast())) + ->cast(test_property(), [1, 'true', 'yes']) + ->toBe([true, true, true]); + +it('properly casts an IntegerCast to array') + ->expect(fn () => new ArrayCast(new IntegerCast())) + ->cast(test_property(), ['1', '5', '10']) + ->toBe([1, 5, 10]); + +it('properly casts an DTOCast', function () { + $castable = new ArrayCast(new DTOCast(ValidatedDTOInstance::class)); + + $johnDto = new ValidatedDTOInstance(['name' => 'John Doe', 'age' => 30]); + $maryDto = new ValidatedDTOInstance(['name' => 'Mary Doe', 'age' => 25]); + + $dataToCast = [ + ['name' => 'John Doe', 'age' => 30], + ['name' => 'Mary Doe', 'age' => 25], + ]; + + $result = $castable->cast(test_property(), $dataToCast); + + expect($result)->each->toBeInstanceOf(ValidatedDTOInstance::class); + + expect($result[0]->toArray())->toEqual($johnDto->toArray()) + ->and($result[1]->toArray())->toEqual($maryDto->toArray()); +});