From 0785f1a049bb2af36e7b4816481d2ce461c9cc51 Mon Sep 17 00:00:00 2001 From: ignace nyamagana butera Date: Sat, 11 Nov 2023 10:57:02 +0100 Subject: [PATCH] Adding Serializer::assignAll --- docs/9.0/reader/record-mapping.md | 11 ++++++++-- src/Reader.php | 7 ++++--- src/ResultSet.php | 6 +++++- src/Serializer.php | 34 +++++++++++++++++++++---------- src/Serializer/CastToArray.php | 2 +- src/Serializer/CastToDate.php | 4 ++-- src/Serializer/CastToEnum.php | 4 ++-- 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/docs/9.0/reader/record-mapping.md b/docs/9.0/reader/record-mapping.md index 9386133f..2503a607 100644 --- a/docs/9.0/reader/record-mapping.md +++ b/docs/9.0/reader/record-mapping.md @@ -12,11 +12,12 @@ title: Deserializing a Tabular Data record into an object To work with objects instead of arrays the `Serializer` class is introduced to expose a text based deserialization mechanism for tabular data. -The class exposes three (3) methods to ease `array` to `object` conversion: +The class exposes four (4) methods to ease `array` to `object` conversion: - `Serializer::deserializeAll` which converts a collection of records into a collection of instances of a specified class. - `Serializer::deserialize` which converts a single record into a new instance of the specified class. - `Serializer::assign` which is a syntactic sugar static method to use in place of `Serializer::deserialize`. +- `Serializer::assignAll` which is a syntactic sugar static method to use in place of `Serializer::deserializeAll`. ```php use League\Csv\Serializer; @@ -30,7 +31,7 @@ $record = [ $serializer = new Serializer(Weather::class, ['date', 'temperature', 'place']); $weather = $serializer->deserialize($record); -// you can use the syntactic sugar method as an alternative +// you can use the syntactic sugar method `assign` as an alternative // if you only need to do it once $weather = Serializer::assign(Weather::class, $record); @@ -40,6 +41,12 @@ $collection = [$record]; foreach ($serializer->deserializeAll($collection) as $weather) { // each $weather entry will be an instance of the Weather class; } + +// you can use the syntactic sugar method `assignAll` as an alternative +// if you only need to do it once +foreach (Serializer::assignAll(Weather::class, $records, ['date', 'temperature', 'place']) as $weather) { + // each $weather entry will be an instance of the Weather class; +} ``` If you are working with a class which implements the `TabularDataReader` interface you can use this functionality diff --git a/src/Reader.php b/src/Reader.php index cd0d5351..16de14fb 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -449,10 +449,11 @@ public function getObjects(string $className, array $header = []): Iterator { /** @var array $header */ $header = $this->prepareHeader($header); - $serializer = new Serializer($className, $header); - return $serializer->deserializeAll( - $this->combineHeader($this->prepareRecords(), $header) + return Serializer::assignAll( + $className, + $this->combineHeader($this->prepareRecords(), $header), + $header ); } diff --git a/src/ResultSet.php b/src/ResultSet.php index d0599a46..3e8a618e 100644 --- a/src/ResultSet.php +++ b/src/ResultSet.php @@ -254,7 +254,11 @@ public function getObjects(string $className, array $header = []): Iterator { $header = $this->prepareHeader($header); - return (new Serializer($className, $header))->deserializeAll($this->combineHeader($header)); + return Serializer::assignAll( + $className, + $this->combineHeader($header), + $header + ); } /** diff --git a/src/Serializer.php b/src/Serializer.php index be172220..d2984c4c 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -44,10 +44,10 @@ final class Serializer { private readonly ReflectionClass $class; - /** @var array */ - private readonly array $propertySetters; /** @var array */ private readonly array $properties; + /** @var non-empty-array */ + private readonly array $propertySetters; /** * @param class-string $className @@ -61,12 +61,6 @@ public function __construct(string $className, array $propertyNames = []) $this->class = new ReflectionClass($className); $this->properties = $this->class->getProperties(); $this->propertySetters = $this->findPropertySetters($propertyNames); - - //if converters is empty it means the Serializer - //was unable to detect properties to assign - if ([] === $this->propertySetters) { - throw new MappingFailed('No properties or method setters were found eligible on the class `'.$className.'` to be used for type casting.'); - } } /** @@ -82,6 +76,19 @@ public static function assign(string $className, array $record): object return (new self($className, array_keys($record)))->deserialize($record); } + /** + * @param class-string $className + * @param array $propertyNames + * + * @throws MappingFailed + * @throws ReflectionException + * @throws TypeCastingFailed + */ + public static function assignAll(string $className, iterable $records, array $propertyNames = []): Iterator + { + return (new self($className, $propertyNames))->deserializeAll($records); + } + public function deserializeAll(iterable $records): Iterator { $check = true; @@ -142,7 +149,7 @@ private function assertObjectIsInValidState(object $object): void * * @throws MappingFailed * - * @return array + * @return non-empty-array */ private function findPropertySetters(array $propertyNames): array { @@ -193,6 +200,12 @@ private function findPropertySetters(array $propertyNames): array } } + //if converters is empty it means the Serializer + //was unable to detect properties to assign + if ([] === $propertySetters) { + throw new MappingFailed('No properties or method setters were found eligible on the class `'.$this->class->getName().'` to be used for type casting.'); + } + return $propertySetters; } @@ -297,7 +310,7 @@ private function resolveTypeCasting(ReflectionType $reflectionType, array $argum throw $exception; } - throw new MappingFailed('Unable to instantiate a casting mechanism. Please verify your casting arguments', 0, $exception); + throw new MappingFailed(message:'Unable to instantiate a casting mechanism. Please verify your casting arguments', previous: $exception); } } @@ -316,7 +329,6 @@ private function getTypeCasting(Cell $cell, ReflectionProperty|ReflectionMethod }; $typeCaster = $cell->cast; - if (null !== $typeCaster) { $cast = new $typeCaster((string) $type, ...$cell->castArguments); if (!$cast instanceof TypeCasting) { diff --git a/src/Serializer/CastToArray.php b/src/Serializer/CastToArray.php index 3e5e23c6..6f7ba176 100644 --- a/src/Serializer/CastToArray.php +++ b/src/Serializer/CastToArray.php @@ -102,7 +102,7 @@ public function toVariable(?string $value): ?array return $result; } catch (JsonException $exception) { - throw new TypeCastingFailed('Unable to cast the given data `'.$value.'` to a PHP array.', 0, $exception); + throw new TypeCastingFailed(message: 'Unable to cast the given data `'.$value.'` to a PHP array.', previous: $exception); } } diff --git a/src/Serializer/CastToDate.php b/src/Serializer/CastToDate.php index 5dea6235..9d0bc3cc 100644 --- a/src/Serializer/CastToDate.php +++ b/src/Serializer/CastToDate.php @@ -58,7 +58,7 @@ public function __construct( $this->timezone = is_string($timezone) ? new DateTimeZone($timezone) : $timezone; $this->default = (null !== $default) ? $this->cast($default) : $default; } catch (Throwable $exception) { - throw new MappingFailed('The configuration option for `'.self::class.'` are invalid.', 0, $exception); + throw new MappingFailed(message: 'The configuration option for `'.self::class.'` are invalid.', previous: $exception); } } @@ -91,7 +91,7 @@ private function cast(string $value): DateTimeImmutable|DateTime throw $exception; } - throw new TypeCastingFailed('Unable to cast the given data `'.$value.'` to a PHP DateTime related object.', 0, $exception); + throw new TypeCastingFailed(message: 'Unable to cast the given data `'.$value.'` to a PHP DateTime related object.', previous: $exception); } return $date; diff --git a/src/Serializer/CastToEnum.php b/src/Serializer/CastToEnum.php index ccd9059f..5181fa8f 100644 --- a/src/Serializer/CastToEnum.php +++ b/src/Serializer/CastToEnum.php @@ -47,7 +47,7 @@ public function __construct( try { $this->default = (null !== $default) ? $this->cast($default) : $default; } catch (TypeCastingFailed $exception) { - throw new MappingFailed('The configuration option for `'.self::class.'` are invalid.', 0, $exception); + throw new MappingFailed(message:'The configuration option for `'.self::class.'` are invalid.', previous: $exception); } } @@ -78,7 +78,7 @@ private function cast(string $value): BackedEnum|UnitEnum return $this->class::from($backedValue); } catch (Throwable $exception) { - throw new TypeCastingFailed('Unable to cast to `'.$this->class.'` the value `'.$value.'`.', 0, $exception); + throw new TypeCastingFailed(message: 'Unable to cast to `'.$this->class.'` the value `'.$value.'`.', previous: $exception); } } }