diff --git a/src/Actions/DataMapperAction.php b/src/Actions/DataMapperAction.php new file mode 100644 index 0000000..1ab2651 --- /dev/null +++ b/src/Actions/DataMapperAction.php @@ -0,0 +1,21 @@ +mapper() + ->map( + signature: $signature, + source: Source::array(data: $data)->camelCaseKeys() + ); + } +} diff --git a/src/Contracts/DataMapper.php b/src/Contracts/DataMapper.php new file mode 100644 index 0000000..243603c --- /dev/null +++ b/src/Contracts/DataMapper.php @@ -0,0 +1,16 @@ + $data + * @param class-string | string $signature + * + * @throws MappingError + */ + public function execute(string $signature, array $data): mixed; +} diff --git a/src/DiamondConsoleServiceProvider.php b/src/DiamondConsoleServiceProvider.php index 4e08f79..b9542c2 100644 --- a/src/DiamondConsoleServiceProvider.php +++ b/src/DiamondConsoleServiceProvider.php @@ -6,6 +6,8 @@ use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; +use KoalaFacade\DiamondConsole\Actions\DataMapperAction; +use KoalaFacade\DiamondConsole\Contracts\DataMapper; use Symfony\Component\Finder\SplFileInfo; class DiamondConsoleServiceProvider extends ServiceProvider @@ -20,6 +22,8 @@ public function boot(): void public function register(): void { $this->mergeConfigFrom(path: __DIR__ . '/../config/diamond.php', key: 'diamond'); + + $this->app->singletonIf(abstract: DataMapper::class, concrete: DataMapperAction::class); } protected function registerPublishers(): void diff --git a/src/Foundation/Action.php b/src/Foundation/Action.php index f10fd04..ada6985 100644 --- a/src/Foundation/Action.php +++ b/src/Foundation/Action.php @@ -2,7 +2,7 @@ namespace KoalaFacade\DiamondConsole\Foundation; -readonly abstract class Action +abstract readonly class Action { /** * Resolve an action class diff --git a/src/Foundation/DataTransferObject.php b/src/Foundation/DataTransferObject.php index 539e847..bd0fb7c 100644 --- a/src/Foundation/DataTransferObject.php +++ b/src/Foundation/DataTransferObject.php @@ -37,6 +37,14 @@ protected function toExcludedPropertiesOnUpdate(): array return []; } + /** + * Resolve result array-key of toArray method from behaviour + */ + protected function resolveArrayKey(string $key): string + { + return Str::snake(value: $key); + } + /** * The method that will resolve the inheritance properties * naming to snake case that can fit with database column naming @@ -59,18 +67,8 @@ public function toArray(): array ->toArray(); } - /** - * Resolve result array-key of toArray method from behaviour - */ - protected function resolveArrayKey(string $key): string - { - return Str::snake(value: $key); - } - /** * Die and dump the current Data. - * - * @return never */ public function dd(): never { @@ -79,9 +77,6 @@ public function dd(): never /** * Abilities to orchestrate the Data - * - * @param mixed ...$values - * @return static */ public function recycle(mixed ...$values): static { diff --git a/src/Foundation/DataTransferObject/HasResolvable.php b/src/Foundation/DataTransferObject/HasResolvable.php index ab6cc87..49a7964 100644 --- a/src/Foundation/DataTransferObject/HasResolvable.php +++ b/src/Foundation/DataTransferObject/HasResolvable.php @@ -8,6 +8,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Arr; use Illuminate\Support\Str; +use KoalaFacade\DiamondConsole\Contracts\DataMapper; trait HasResolvable { @@ -47,15 +48,12 @@ public static function resolveFrom(FormRequest | Model | array $abstract): stati * @param array $data * * @throws MappingError + * + * @deprecated use hydrate(array $data) instead */ 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; + return static::hydrate($data); } /** @@ -92,6 +90,8 @@ public static function resolveFromModel(Model $model): static } /** + * @deprecated + * * Resolve all array key form according the config * * @template TArrayKey of array-key @@ -128,4 +128,28 @@ protected static function resolveArrayKeyOfInput(string $key): string { return Str::camel(value: $key); } + + /** + * @param array $data + * + * Hydrate incoming data to resolve unstructured data + * + * @throws MappingError + * + * @template TKey of array-key + * @template TValue + */ + public static function hydrate(array $data): static + { + /** @var DataMapper $dataMapper */ + $dataMapper = resolve(name: DataMapper::class); + + /** @var static $instance */ + $instance = $dataMapper->execute( + signature: static::class, + data: $data + ); + + return $instance; + } } diff --git a/tests/Unit/DataTransferObjects/DataTransferObjectTest.php b/tests/Unit/DataTransferObjects/DataTransferObjectTest.php index 4ef263c..3640375 100644 --- a/tests/Unit/DataTransferObjects/DataTransferObjectTest.php +++ b/tests/Unit/DataTransferObjects/DataTransferObjectTest.php @@ -2,6 +2,7 @@ use Composer\InstalledVersions; use Illuminate\Support\Arr; +use KoalaFacade\DiamondConsole\Contracts\DataMapper; use Tests\Unit\DataTransferObjects\Fixtures\GenderEnum; use Tests\Unit\DataTransferObjects\Fixtures\RoleData; use Tests\Unit\DataTransferObjects\Fixtures\UserData; @@ -84,7 +85,7 @@ ) ->tap(callable: function () { $data = UserData::resolve(data: [ - 'name' => 'Kevin' + 'name' => 'Kevin', ]); $addresses = [ @@ -108,7 +109,7 @@ ) ->tap(callable: function () { $data = UserData::resolve(data: [ - 'name' => 'Kevin' + 'name' => 'Kevin', ]); $addresses = [ @@ -132,7 +133,7 @@ ) ->tap(callable: function () { $data = UserData::resolve(data: [ - 'name' => 'Kevin' + 'name' => 'Kevin', ]); $roleData = new RoleData(name: 'Maintainer'); @@ -160,4 +161,37 @@ ->tap( callback: fn (UserData $data) => expect($roleData->name)->toBe($roleData->name) ); - }); \ No newline at end of file + }); + +it(description: 'can hydrate data') + ->group('unit', 'dto') + ->tap(callable: function () { + $data = UserData::hydrate(data: [ + 'gender' => GenderEnum::Female, + ]); + + expect(value: $data->gender)->toBe(expected: GenderEnum::Female); + }); + +it(description: 'can change hydrate data mapper implementation') + ->group('unit', 'dto') + ->tap(callable: function () { + app()->instance( + abstract: DataMapper::class, + instance: new class implements DataMapper + { + public function execute(string $signature, array $data): mixed + { + return new $signature( + gender: $data['gender'] + ); + } + } + ); + + $data = UserData::hydrate(data: [ + 'gender' => GenderEnum::Female, + ]); + + expect(value: $data->gender)->toBe(expected: GenderEnum::Female); + });