Skip to content

Commit

Permalink
Merge pull request #77 from pemudakoding/main
Browse files Browse the repository at this point in the history
Refactor DTO using  cuyz/valinor to better implementation
  • Loading branch information
pemudakoding authored Feb 27, 2023
2 parents 93baaf6 + 841fc99 commit 0a16eb1
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 15 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"illuminate/support": "^9.41 | ^10.0.0",
"illuminate/console": "^9.41 | ^10.0.0",
"illuminate/filesystem": "^9.41 | ^10.0.0",
"illuminate/container": "^9.42 | ^10.0.0"
"illuminate/container": "^9.42 | ^10.0.0",
"cuyz/valinor": "^1.3"
},
"require-dev": {
"laravel/pint": "^1.2",
Expand Down
2 changes: 1 addition & 1 deletion src/Foundation/DataTransferObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ public function toArray(): array
*/
protected function resolveArrayKey(string $key): string
{
return Str::snake($key);
return Str::snake(value: $key);
}
}
106 changes: 93 additions & 13 deletions src/Foundation/DataTransferObject/HasResolvable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,25 @@

namespace KoalaFacade\DiamondConsole\Foundation\DataTransferObject;

use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\MapperBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

trait HasResolvable
{
/**
* Resolve unstructured data from array
*
* @template TKey of array-key
* @template TValue
*
* @param array<TKey, TValue> $data
*/
public static function resolveFromArray(array $data): static
{
return throw new \RuntimeException();
}

/**
* Resolve unstructured data from polymorphism types
*
* @template TKey of array-key
* @template TValue
*
* @param FormRequest | Model | array<TKey, TValue> $abstract
* @return static
*
* @throws MappingError
*/
public static function resolveFrom(FormRequest | Model | array $abstract): static
{
Expand All @@ -46,8 +39,50 @@ public static function resolveFrom(FormRequest | Model | array $abstract): stati
return throw new \RuntimeException;
}

/**
* Resolve unstructured data from array
*
* @template TKey of array-key
* @template TValue
*
* @param array<TKey, TValue> $data
* @return static
*
* @throws MappingError
*/
public static function resolve(array $data): static
{
/** @var static $instance */
$instance = (new MapperBuilder())
->mapper()
->map(signature: static::class, source: static::resolveTheArrayKeyForm(data: $data));

return $instance;
}

/**
* Resolve unstructured data from array
*
* @template TKey of array-key
* @template TValue
*
* @param array<TKey, TValue> $data
* @return static
*
* @throws MappingError
*
* @deprecated can use resolve()
*/
public static function resolveFromArray(array $data): static
{
return static::resolve($data);
}

/**
* Resolve unstructured data from FormRequest
*
* @param FormRequest $request
* @return static
*/
public static function resolveFromFormRequest(FormRequest $request): static
{
Expand All @@ -56,9 +91,54 @@ public static function resolveFromFormRequest(FormRequest $request): static

/**
* Resolve unstructured data from Model
*
* @param Model $model
* @return static
*/
public static function resolveFromModel(Model $model): static
{
return throw new \RuntimeException;
}

/**
* Resolve all array key form according the config
*
* @template TArrayKey of array-key
* @template TArrayValue
*
* @param array<TArrayKey, TArrayValue> $data
* @return array<TArrayKey, mixed>
*/
protected static function resolveTheArrayKeyForm(array $data): array
{
$array = [];

foreach ($data as $key => $value) {
$key = static::resolveArrayKeyOfInput(key: $key);

if (Arr::accessible(value: $value)) {
/** @var array<TArrayKey, TArrayValue> $valueContainsArray */
$valueContainsArray = $value;

$array[$key] = static::resolveTheArrayKeyForm(data: $valueContainsArray);

continue;
}

$array[$key] = $value;
}

return $array;
}

/**
* Resolve the input of array key to constructor naming
*
* @param string $key
* @return string
*/
protected static function resolveArrayKeyOfInput(string $key): string
{
return Str::camel(value: $key);
}
}
77 changes: 77 additions & 0 deletions tests/Unit/DataTransferObjects/DataTransferObjectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

use Illuminate\Support\Arr;
use Tests\Unit\DataTransferObjects\Fixtures\GenderEnum;
use Tests\Unit\DataTransferObjects\Fixtures\RoleData;
use Tests\Unit\DataTransferObjects\Fixtures\UserData;

it(description: 'can resolve From Array')
->tap(callable: function () {
$data = UserData::resolveFromArray(data: [
'name' => 'Stiven Katuuk',
'roles' => null,
]);

expect(value: $data->name)->toBe(expected: 'Stiven Katuuk');
})
->group('unit', 'dto');

it(description: 'can map to sub dto as object')
->tap(callable: function () {
$data = UserData::resolveFromArray(data: [
'name' => 'Stiven Katuuk',
'main_role' => ['name' => 'Moderator'],
]);

expect(value: $data->mainRole)->toBeInstanceOf(class: RoleData::class);
})
->group('unit', 'dto');

it(description: 'can map to sub dto as array')
->tap(callable: function () {
$data = UserData::resolveFromArray(data: [
'name' => 'Stiven Katuuk',
'roles' => [
['name' => 'Admin'],
],
]);

expect(value: Arr::first($data->roles))->toBeInstanceOf(class: RoleData::class);
})
->group('unit', 'dto');

it(description: 'can map to int')
->tap(callable: function () {
$data = UserData::resolveFromArray(data: [
'age' => 19,
]);

expect(value: $data->age)->toBeInt();
})
->group('unit', 'dto');

it(description: 'can map to enum')
->tap(callable: function () {
$data = UserData::resolveFromArray(data: [
'gender' => GenderEnum::Female,
]);

expect(value: $data->gender)->toBe(expected: GenderEnum::Female);
})
->group('unit', 'dto');

it(description: 'can map array and can resolve the key')
->tap(callable: function () {
$data = UserData::resolveFromArray(data: [
'addresses' => [
'main_address' => 'where',
'main_address_1' => 'where',
],
]);

expect(value: $data->addresses)->toMatchArray(array: [
'mainAddress' => 'where',
'mainAddress1' => 'where',
]);
})
->group('unit', 'dto');
10 changes: 10 additions & 0 deletions tests/Unit/DataTransferObjects/Fixtures/GenderEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Tests\Unit\DataTransferObjects\Fixtures;

enum GenderEnum
{
case Female;

case Male;
}
11 changes: 11 additions & 0 deletions tests/Unit/DataTransferObjects/Fixtures/RoleData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Tests\Unit\DataTransferObjects\Fixtures;

class RoleData
{
public function __construct(
public string | null $name,
) {
}
}
20 changes: 20 additions & 0 deletions tests/Unit/DataTransferObjects/Fixtures/UserData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Tests\Unit\DataTransferObjects\Fixtures;

use KoalaFacade\DiamondConsole\Foundation\DataTransferObject;

readonly class UserData extends DataTransferObject
{
public function __construct(
public string | null $name = null,
/** @var array<int, RoleData> | null $roles */
public array | null $roles = null,
public RoleData | null $mainRole = null,
public int | null $age = null,
public GenderEnum | null $gender = null,
/** @var array<string, string> | null $address */
public array | null $addresses = null
) {
}
}

0 comments on commit 0a16eb1

Please sign in to comment.