From 28c11a0eef4bda4ce0dee521e45383faec26418d Mon Sep 17 00:00:00 2001 From: david_smith Date: Mon, 21 Oct 2024 16:54:51 -0400 Subject: [PATCH 1/4] Add re-mapping support in `DataModel.php` --- src/DataModel.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/DataModel.php b/src/DataModel.php index cb81ebb..8534b4f 100644 --- a/src/DataModel.php +++ b/src/DataModel.php @@ -206,16 +206,18 @@ public static function from(iterable|object|null $context = []): self $Attribute = ($ReflectionProperty->getAttributes(Describe::class)[0] ?? null); /** @var Describe $Describe */ $Describe = $Attribute?->newInstance(); - $property_name = $ReflectionProperty->getName(); + $context_key = $Describe->from ?? $ReflectionProperty->getName(); /** Property-level Pre Hook */ if (isset($Describe->pre)) { - ($Describe->pre)($context[$property_name] ?? [], $context, $Attribute, $ReflectionProperty); + ($Describe->pre)($context[$context_key] ?? [], $context, $Attribute, $ReflectionProperty); } + $property_name = $ReflectionProperty->getName(); + /** Property-level Cast */ if (isset($Describe->cast) && $context) { - $self->{$property_name} = ($Describe->cast)($context[$property_name] ?? [], $context, $Attribute, $ReflectionProperty); + $self->{$property_name} = ($Describe->cast)($context[$context_key] ?? [], $context, $Attribute, $ReflectionProperty); /** Property-level Post Hook */ if (isset($Describe->post)) { @@ -227,7 +229,7 @@ public static function from(iterable|object|null $context = []): self /** Property-level Post Hook */ if (isset($Describe->post)) { - $self->{$property_name} = $context[$property_name]; + $self->{$property_name} = $context[$context_key]; ($Describe->post)($self->{$property_name}, $context, $Attribute, $ReflectionProperty); continue; } @@ -235,12 +237,12 @@ public static function from(iterable|object|null $context = []): self /** Method-level Cast */ if (isset($methods[$property_name]) && $context) { $self->{$property_name} = - $self->{$methods[$property_name]}($context[$property_name] ?? null, $context, $Attribute, $ReflectionProperty); + $self->{$methods[$property_name]}($context[$context_key] ?? null, $context, $Attribute, $ReflectionProperty); continue; } /** When a property name does not match a key name */ - if (!array_key_exists($property_name, $context)) { + if (!array_key_exists($context_key, $context)) { if ($Describe->default ?? false) { $self->{$property_name} = $Describe->default; continue; @@ -258,7 +260,7 @@ public static function from(iterable|object|null $context = []): self $ReflectionType = $ReflectionProperty->getType(); /** Assigns value when no type or union type is defined. */ if (!$ReflectionType || $ReflectionType instanceof ReflectionUnionType) { - $self->{$property_name} = $context[$property_name]; + $self->{$property_name} = $context[$context_key]; continue; } @@ -266,21 +268,21 @@ public static function from(iterable|object|null $context = []): self /** Class-level cast */ if ($ClassDescribe?->cast[$property_type] ?? false) { $self->{$property_name} = - $ClassDescribe?->cast[$property_type]($context[$property_name], $context, $ClassDescribeArguments); + $ClassDescribe?->cast[$property_type]($context[$context_key], $context, $ClassDescribeArguments); continue; } /** Call the static method from(). */ if (is_callable([$property_type, 'from']) && method_exists($property_type, 'from')) { $self->{$property_name} = $property_type::from( - $context[$property_name] instanceof UnitEnum - ? $context[$property_name]->value - : $context[$property_name] + $context[$context_key] instanceof UnitEnum + ? $context[$context_key]->value + : $context[$context_key] ); continue; } - $self->{$property_name} = $context[$property_name]; + $self->{$property_name} = $context[$context_key]; } return $self; From d345b62b14cbde6e80d6a6e409008f41699fc612 Mon Sep 17 00:00:00 2001 From: david_smith Date: Mon, 21 Oct 2024 16:54:59 -0400 Subject: [PATCH 2/4] Add re-mapping support in `Describe.php` --- src/Describe.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Describe.php b/src/Describe.php index de633bc..7db94a8 100644 --- a/src/Describe.php +++ b/src/Describe.php @@ -102,6 +102,7 @@ #[Attribute] class Describe { + public string $from; public string|array $cast; public bool $required; public mixed $default; @@ -197,7 +198,7 @@ class Describe * } * ``` * - * @param string|array{'pre': string|string[], 'cast': string|string[], 'post': string|string[], 'required': bool, 'default': mixed, 'missing_as_null': bool}|null| $attributes + * @param string|array{'from': string,'pre': string|string[], 'cast': string|string[], 'post': string|string[], 'required': bool, 'default': mixed, 'missing_as_null': bool}|null| $attributes * * @link https://github.com/zero-to-prod/data-model * From f858b6e4e3b2e3ad9e66fdbec8198f4af5fd4bb5 Mon Sep 17 00:00:00 2001 From: david_smith Date: Mon, 21 Oct 2024 16:55:10 -0400 Subject: [PATCH 3/4] Update `README.md`. --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 312bab7..987113f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Transform data into hydrated objects by [describing](#property-level-cast) how t - [Required Properties](#required-properties): Throw an exception when a property is not set. - [Default Values](#default-values): Set a default property value. - [Nullable Missing Values](#nullable-missing-values): Resolve a missing value as null. +- [Remapping](#re-mapping): Re-map a key to a property of a different name. ## Installation @@ -121,6 +122,8 @@ The `Describe` attribute can accept these arguments. ```php #[\Zerotoprod\DataModel\Describe([ + // Re-map a key to a property of a different name + 'from' => 'key', // Runs before 'cast' 'pre' => [MyClass::class, 'preHook'] // Targets the static method: `MyClass::methodName()` @@ -392,6 +395,28 @@ echo $User->name; // null echo $User->age; // null ``` +## Re-Mapping + +You can map a key to a property of a different name like this: + +```php +use Zerotoprod\DataModel\Describe; + +class User +{ + use \Zerotoprod\DataModel\DataModel; + + #[Describe(['from' => 'firstName'])] + public string $first_name; +} + +$User = User::from([ + 'firstName' => 'John', +]); + +echo $User->first_name; // John +``` + ## Examples ### Array of DataModels From 4fdf7c69f0e67d7b01132eb292c700fb24288f1d Mon Sep 17 00:00:00 2001 From: david_smith Date: Mon, 21 Oct 2024 16:55:17 -0400 Subject: [PATCH 4/4] Add tests. --- tests/Unit/Examples/From/ClassTest.php | 18 ++++++++++++++++++ tests/Unit/Examples/From/User.php | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/Unit/Examples/From/ClassTest.php create mode 100644 tests/Unit/Examples/From/User.php diff --git a/tests/Unit/Examples/From/ClassTest.php b/tests/Unit/Examples/From/ClassTest.php new file mode 100644 index 0000000..50caae6 --- /dev/null +++ b/tests/Unit/Examples/From/ClassTest.php @@ -0,0 +1,18 @@ + '1', + ]); + + $this->assertEquals('1', $User->first_name); + } +} \ No newline at end of file diff --git a/tests/Unit/Examples/From/User.php b/tests/Unit/Examples/From/User.php new file mode 100644 index 0000000..c183f3c --- /dev/null +++ b/tests/Unit/Examples/From/User.php @@ -0,0 +1,15 @@ + 'firstName'])] + public string $first_name; +} \ No newline at end of file