Skip to content

Commit

Permalink
Improve Dernormalizer feature DX
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Nov 23, 2023
1 parent 5d2dfcf commit 7200acf
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 28 deletions.
24 changes: 12 additions & 12 deletions docs/9.0/reader/record-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ use Carbon\CarbonImmutable;
#[Serializer\Cell(
column:'date',
cast:Serializer\CastToDate::class,
castArguments: [
options: [
'format' => '!Y-m-d',
'timezone' => 'Africa/Nairobi'
])
Expand All @@ -148,15 +148,15 @@ The attribute can take up to three (3) arguments which are all optional:

- The `column` argument tells the engine which record key to use via its numeric or name. If not present the property name or the name of the first argument of the `setter` method will be used. In such case, you are required to specify the property names information.
- The `cast` argument which accept the name of a class implementing the `TypeCasting` interface and responsible for type casting the record value. If not present, the mechanism will try to resolve the typecasting based on the property or method argument type.
- The `castArguments` argument enables controlling typecasting by providing extra arguments to the `TypeCasting` class constructor. The argument expects an associative array and relies on named arguments to inject its value to the `TypeCasting` implementing class constructor.
- The `options` argument enables controlling typecasting by providing extra arguments to the `TypeCasting` class constructor. The argument expects an associative array and relies on named arguments to inject its value to the `TypeCasting` implementing class constructor.

<p class="message-notice">You can use the mechanism on a CSV without a header row but it requires
adding a <code>Cell</code> attribute on each property or method needed for the conversion. Or you
can use the optional second argument of <code>TabularDataReader::getObjects</code> to specify the
header value, just like with <code>TabularDataReader::getRecords</code></p>

<p class="message-warning">The <code>reflectionProperty</code> key can not be used with the
<code>castArguments</code> as it is a reserved argument used by the <code>TypeCasting</code> class.</p>
<code>options</code> as it is a reserved argument used by the <code>TypeCasting</code> class.</p>

In any case, if type casting fails, an exception will be thrown.

Expand Down Expand Up @@ -245,7 +245,7 @@ use League\Csv\Serializer\Cell;
#[Cell(
column:1,
cast:Serializer\CastToEnum::class,
castArguments: ['default' => 'Abidjan', 'className' => Place::class]
options: ['default' => 'Abidjan', 'className' => Place::class]
)]
public function setPlace(mixed $place): void
{
Expand Down Expand Up @@ -300,7 +300,7 @@ use League\Csv\Serializer;

#[Serializer\Cell(
cast:Serializer\CastToArray::class,
castArguments: [
options: [
'shape' => 'json',
'flags' => JSON_BIGINT_AS_STRING
])
Expand All @@ -319,7 +319,7 @@ use League\Csv\Serializer;

#[Serializer\Cell(
cast:Serializer\CastToArray::class,
castArguments: [
options: [
'shape' => 'csv',
'delimiter' => ';',
'type' => 'float',
Expand Down Expand Up @@ -369,7 +369,7 @@ To do so specify your casting with the attribute:
use App\Domain\Money
use League\Csv\Serializer;

#[Serializer\Cell(column: 'amount', castArguments: ['default' => 1000_00])]
#[Serializer\Cell(column: 'amount', options: ['default' => 1000_00])]
private ?Naira $amount;
```

Expand All @@ -391,14 +391,14 @@ attribute using the `cast` argument.
The closure signature is the following:

```php
closure(?string $value, bool $isNullable, ...$arguments): mixed;
closure(?string $value, bool $isNullable, ...$options): mixed;
```

where:

- the `$value` is the record value
- the `$isNullable` tells whether the argument or property is nullable
- the `$arguments` are the extra configuration options you can pass to the `Cell` attribute via `castArguments`
- the `$options` are the extra configuration options you can pass to the `Cell` attribute via `options`

To complete the feature you can use `Denormalizer::unregisterType` to remove a registered closure for a specific `type`.

Expand Down Expand Up @@ -428,7 +428,7 @@ use League\Csv\Serializer;
#[Serializer\Cell(
column: 'amount',
cast: App\Domain\Money\CastToNaira::class,
castArguments: ['default' => 20_00]
options: ['default' => 20_00]
)]
private ?Money $naira;
```
Expand All @@ -439,7 +439,7 @@ To do so, you must define a `toVariable` method that will return the correct val

<p class="message-warning"><strong>Of note</strong> The class constructor method must take the property type value as
one of its argument with the name <code>$reflectionProperty</code>. This means you <strong>can not</strong> use the
<code>reflectionProperty</code> as a possible key of the associative array given to <code>castArguments</code></p>
<code>reflectionProperty</code> as a possible key of the associative array given to <code>options</code></p>

```php
<?php
Expand All @@ -462,7 +462,7 @@ final class CastToNaira implements TypeCasting

public function __construct(
ReflectionProperty|ReflectionParameter $reflectionProperty, //always given by the Denormalizer
?int $default = null //can be filled via the Cell castArguments array destructuring
?int $default = null //can be filled via the Cell options array destructuring
) {
if (null !== $default) {
$default = Naira::fromKobos($default);
Expand Down
2 changes: 1 addition & 1 deletion src/Serializer/Cell.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final class Cell
public function __construct(
public readonly string|int|null $column = null,
public readonly ?string $cast = null,
public readonly array $castArguments = []
public readonly array $options = []
) {
}
}
6 changes: 3 additions & 3 deletions src/Serializer/Denormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ private function autoDiscoverPropertySetter(ReflectionMethod|ReflectionProperty
*/
private function findPropertySetter(Cell $cell, ReflectionMethod|ReflectionProperty $accessor, array $propertyNames): PropertySetter
{
if (array_key_exists('reflectionProperty', $cell->castArguments)) {
throw MappingFailed::dueToForbiddenCastArgument();
if (array_key_exists('reflectionProperty', $cell->options)) {
throw MappingFailed::dueToForbiddenOptionName();
}

/** @var ?class-string<TypeCasting> $typeCaster */
Expand Down Expand Up @@ -294,7 +294,7 @@ private function findPropertySetter(Cell $cell, ReflectionMethod|ReflectionPrope
$offset = $index;
}

$arguments = [...$cell->castArguments, ...['reflectionProperty' => $reflectionProperty = match (true) {
$arguments = [...$cell->options, ...['reflectionProperty' => $reflectionProperty = match (true) {
$accessor instanceof ReflectionMethod => $accessor->getParameters()[0],
$accessor instanceof ReflectionProperty => $accessor,
}]];
Expand Down
20 changes: 10 additions & 10 deletions src/Serializer/DenormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function __construct(
public readonly Place $place,
#[Cell(
column: 'date',
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
public readonly DateTimeInterface $observedOn
) {
Expand Down Expand Up @@ -75,7 +75,7 @@ public function __construct(
public readonly Place $place,
#[Cell(
column: 'date',
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
public readonly DateTimeInterface $observedOn
) {
Expand Down Expand Up @@ -107,7 +107,7 @@ public function __construct(
#[Cell(
column: 'date',
cast: CastToDate::class,
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
public readonly DateTimeInterface $observedOn
) {
Expand All @@ -133,7 +133,7 @@ public function __construct(
#[Cell(
column: 'date',
cast: CastToDate::class,
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
private readonly DateTime $observedOn
) {
Expand Down Expand Up @@ -189,7 +189,7 @@ public function __construct(
#[Cell(
column: 'date',
cast: CastToDate::class,
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
private readonly DateTime $observedOn
) {
Expand Down Expand Up @@ -234,7 +234,7 @@ public function __construct(
#[Cell(
column: 'date',
cast: CastToDate::class,
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
private readonly DateTime $observedOn
) {
Expand Down Expand Up @@ -279,7 +279,7 @@ public function __construct(
#[Cell(
column: 'date',
cast: CastToDate::class,
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
public readonly DateTimeInterface $observedOn
) {
Expand All @@ -303,7 +303,7 @@ public function __construct(
#[Cell(
column: 'date',
cast: CastToDate::class,
castArguments: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
options: ['format' => '!Y-m-d', 'timezone' => 'Africa/Kinshasa'],
)]
public readonly DateTimeInterface $observedOn
) {
Expand All @@ -323,7 +323,7 @@ public function testItWillResolveTheObjectWhichDoesNotHaveTypedPropertiesUsingCe
public function __construct(
#[Cell(cast:CastToFloat::class)]
public $temperature,
#[Cell(cast:CastToEnum::class, castArguments: ['className' => Place::class])]
#[Cell(cast:CastToEnum::class, options: ['className' => Place::class])]
public $place,
#[Cell(cast: CastToDate::class)]
public $observedOn
Expand Down Expand Up @@ -364,7 +364,7 @@ public function __construct(
public readonly string $prenoms,
private readonly int $nombre,
public readonly string $sexe,
#[Cell(castArguments: ['format' => '!Y'])]
#[Cell(options: ['format' => '!Y'])]
public SplFileObject $annee
) {
}
Expand Down
4 changes: 2 additions & 2 deletions src/Serializer/MappingFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public static function dueToInvalidTypeCastingClass(string $typeCaster): self
return new self('`'.$typeCaster.'` must be an resolvable class implementing the `'.TypeCasting::class.'` interface.');
}

public static function dueToForbiddenCastArgument(): self
public static function dueToForbiddenOptionName(): self
{
return new self('The key `reflectionProperty` can not be used with `castArguments`.');
return new self('The key `reflectionProperty` can not be used on the `'.Cell::class.'` options property.');
}
}

0 comments on commit 7200acf

Please sign in to comment.