Skip to content

Commit 9d05280

Browse files
Merge pull request #53 from WendellAdriel/fix-dto-transform
Fix issue when transforming DTOs with nested DTOs into array or json
2 parents 010a519 + 95cc67a commit 9d05280

File tree

4 files changed

+126
-13
lines changed

4 files changed

+126
-13
lines changed

src/Concerns/DataResolver.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static function fromModel(Model $model): static
5656
*/
5757
public static function fromCommandArguments(Command $command): static
5858
{
59-
return new static($command->arguments());
59+
return new static(self::filterArguments($command->arguments()));
6060
}
6161

6262
/**
@@ -72,6 +72,18 @@ public static function fromCommandOptions(Command $command): static
7272
*/
7373
public static function fromCommand(Command $command): static
7474
{
75-
return new static(array_merge($command->arguments(), $command->options()));
75+
return new static(array_merge(self::filterArguments($command->arguments()), $command->options()));
76+
}
77+
78+
private static function filterArguments(array $arguments): array
79+
{
80+
$result = [];
81+
foreach ($arguments as $key => $value) {
82+
if (! is_numeric($key)) {
83+
$result[$key] = $value;
84+
}
85+
}
86+
87+
return $result;
7688
}
7789
}

src/SimpleDTO.php

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ protected function passedValidation(): void
177177
$formatted = $this->shouldReturnNull($key, $value)
178178
? null
179179
: $this->castValue($casts[$key], $key, $value);
180+
180181
$this->{$key} = $formatted;
181182
$this->validatedData[$key] = $formatted;
182183
}
@@ -343,10 +344,6 @@ private function buildDataForExport(): array
343344

344345
private function mapDTOData(array $mapping, array $data): array
345346
{
346-
if (empty($mapping)) {
347-
return $data;
348-
}
349-
350347
$mappedData = [];
351348
foreach ($data as $key => $value) {
352349
$properties = $this->getMappedProperties($mapping, $key);
@@ -364,7 +361,9 @@ private function mapDTOData(array $mapping, array $data): array
364361
? $mapping[$key]
365362
: $key;
366363

367-
$mappedData[$property] = $value;
364+
$mappedData[$property] = $this->isArrayable($value)
365+
? $this->formatArrayableValue($value)
366+
: $value;
368367
}
369368

370369
return $mappedData;
@@ -399,15 +398,26 @@ private function isArrayable(mixed $value): bool
399398

400399
private function formatArrayableValue(mixed $value): array
401400
{
402-
if (is_array($value)) {
403-
return $value;
404-
}
401+
return match (true) {
402+
is_array($value) => $value,
403+
$value instanceof Collection => $value->toArray(),
404+
$value instanceof Model => $value->toArray(),
405+
$value instanceof SimpleDTO => $this->transformDTOToArray($value),
406+
is_object($value) => (array) $value,
407+
default => [],
408+
};
409+
}
405410

406-
if (is_object($value)) {
407-
return (array) $value;
411+
private function transformDTOToArray(SimpleDTO $dto): array
412+
{
413+
$result = [];
414+
foreach ($dto->validatedData as $key => $value) {
415+
$result[$key] = $this->isArrayable($value)
416+
? $this->formatArrayableValue($value)
417+
: $value;
408418
}
409419

410-
return $value->toArray();
420+
return $result;
411421
}
412422

413423
private function initConfig(): void

tests/Datasets/UserNestedDTO.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WendellAdriel\ValidatedDTO\Tests\Datasets;
6+
7+
use WendellAdriel\ValidatedDTO\Casting\DTOCast;
8+
use WendellAdriel\ValidatedDTO\ValidatedDTO;
9+
10+
class UserNestedDTO extends ValidatedDTO
11+
{
12+
public NameDTO $name;
13+
14+
public string $email;
15+
16+
protected function rules(): array
17+
{
18+
return [
19+
'name' => ['required', 'array'],
20+
'email' => ['required', 'email'],
21+
];
22+
}
23+
24+
protected function defaults(): array
25+
{
26+
return [];
27+
}
28+
29+
protected function casts(): array
30+
{
31+
return [
32+
'name' => new DTOCast(NameDTO::class),
33+
];
34+
}
35+
}

tests/Unit/ValidatedDTOTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
use WendellAdriel\ValidatedDTO\Tests\Datasets\NullableDTO;
1717
use WendellAdriel\ValidatedDTO\Tests\Datasets\User;
1818
use WendellAdriel\ValidatedDTO\Tests\Datasets\UserDTO;
19+
use WendellAdriel\ValidatedDTO\Tests\Datasets\UserNestedDTO;
1920
use WendellAdriel\ValidatedDTO\Tests\Datasets\ValidatedDTOInstance;
2021
use WendellAdriel\ValidatedDTO\ValidatedDTO;
2122

2223
beforeEach(function () {
2324
$this->subject_name = fake()->name;
25+
$this->subject_email = fake()->unique()->safeEmail;
2426
});
2527

2628
it('instantiates a ValidatedDTO validating its data', function () {
@@ -218,6 +220,60 @@ public function __invoke()
218220
->toBe(json_encode(['name' => $this->subject_name], JSON_PRETTY_PRINT));
219221
});
220222

223+
it('validates that the ValidatedDTO with nested data can be converted into an array', function () {
224+
$validatedDTO = new UserNestedDTO([
225+
'name' => [
226+
'first_name' => $this->subject_name,
227+
'last_name' => 'Doe',
228+
],
229+
'email' => $this->subject_email,
230+
]);
231+
232+
expect($validatedDTO)->toArray()
233+
->toBe([
234+
'name' => [
235+
'first_name' => $this->subject_name,
236+
'last_name' => 'Doe',
237+
],
238+
'email' => $this->subject_email,
239+
]);
240+
});
241+
242+
it('validates that the ValidatedDTO with nested data can be converted into a JSON string', function () {
243+
$validatedDTO = new UserNestedDTO([
244+
'name' => [
245+
'first_name' => $this->subject_name,
246+
'last_name' => 'Doe',
247+
],
248+
'email' => $this->subject_email,
249+
]);
250+
251+
expect($validatedDTO)->toJson()
252+
->toBe('{"name":{"first_name":"' . $this->subject_name . '","last_name":"Doe"},"email":"' . $this->subject_email . '"}');
253+
});
254+
255+
it('validates that the ValidatedDTO with nested data can be converted into a pretty JSON string', function () {
256+
$validatedDTO = new UserNestedDTO([
257+
'name' => [
258+
'first_name' => $this->subject_name,
259+
'last_name' => 'Doe',
260+
],
261+
'email' => $this->subject_email,
262+
]);
263+
264+
expect($validatedDTO)->toPrettyJson()
265+
->toBe(json_encode(
266+
[
267+
'name' => [
268+
'first_name' => $this->subject_name,
269+
'last_name' => 'Doe',
270+
],
271+
'email' => $this->subject_email,
272+
],
273+
JSON_PRETTY_PRINT
274+
));
275+
});
276+
221277
it('validates that the ValidatedDTO can be converted into an Eloquent Model', function () {
222278
$validatedDTO = new ValidatedDTOInstance(['name' => $this->subject_name]);
223279

0 commit comments

Comments
 (0)