Skip to content

Commit

Permalink
Adding Serializer::assignAll
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Nov 11, 2023
1 parent 9e03786 commit 0785f1a
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 22 deletions.
11 changes: 9 additions & 2 deletions docs/9.0/reader/record-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -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
Expand Down
7 changes: 4 additions & 3 deletions src/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -449,10 +449,11 @@ public function getObjects(string $className, array $header = []): Iterator
{
/** @var array<string> $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
);
}

Expand Down
6 changes: 5 additions & 1 deletion src/ResultSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}

/**
Expand Down
34 changes: 23 additions & 11 deletions src/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@
final class Serializer
{
private readonly ReflectionClass $class;
/** @var array<PropertySetter> */
private readonly array $propertySetters;
/** @var array<ReflectionProperty> */
private readonly array $properties;
/** @var non-empty-array<PropertySetter> */
private readonly array $propertySetters;

/**
* @param class-string $className
Expand All @@ -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.');
}
}

/**
Expand All @@ -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<string> $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;
Expand Down Expand Up @@ -142,7 +149,7 @@ private function assertObjectIsInValidState(object $object): void
*
* @throws MappingFailed
*
* @return array<string, PropertySetter>
* @return non-empty-array<string, PropertySetter>
*/
private function findPropertySetters(array $propertyNames): array
{
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/Serializer/CastToArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Serializer/CastToDate.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/Serializer/CastToEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
}
}

0 comments on commit 0785f1a

Please sign in to comment.