Skip to content

Commit

Permalink
Rename Serializer class to Deserializer
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Nov 15, 2023
1 parent 3ce4864 commit 23623fe
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 138 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ All Notable changes to `Csv` will be documented in this file
- `ResultSet::fromRecords`
- `Stream::setMaxLineLen`
- `Stream::getMaxLineLen`
- `League\Csv\Serializer` to allow casting records to objects [#508](https://github.com/thephpleague/csv/issues/508)
- `League\Csv\Deserializer` to allow casting records to objects [#508](https://github.com/thephpleague/csv/issues/508)

### Deprecated

Expand Down
83 changes: 49 additions & 34 deletions docs/9.0/reader/record-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ title: Deserializing a Tabular Data record into an object

## Assign an array to an object

To work with objects instead of arrays the `Serializer` class is introduced to expose a
To work with objects instead of arrays the `Deserializer` class is introduced to expose a
text based deserialization mechanism for tabular data.

The class exposes four (4) methods to ease `array` to `object` conversion:

- `Serializer::deserializeAll` and `Serializer::assignAll` which convert a collection of records into a collection of instances of a specified class.
- `Serializer::deserialize` and `Serializer::assign` which convert a single record into a new instance of the specified class.
- `Deserializer::deserializeAll` and `Deserializer::assignAll` which convert a collection of records into a collection of instances of a specified class.
- `Deserializer::deserialize` and `Deserializer::assign` which convert a single record into a new instance of the specified class.

```php
use League\Csv\Serializer;
use League\Csv\Deserializer;

$record = [
'date' => '2023-10-30',
Expand All @@ -29,7 +29,7 @@ $record = [
//a complete collection of records as shown below
$collection = [$record];
//we first instantiate the serializer
$serializer = new Serializer(Weather::class, ['date', 'temperature', 'place']);
$serializer = new Deserializer(Weather::class, ['date', 'temperature', 'place']);

$weather = $serializer->deserialize($record); //we convert 1 record into 1 instance
foreach ($serializer->deserializeAll($collection) as $weather) {
Expand All @@ -38,9 +38,9 @@ foreach ($serializer->deserializeAll($collection) as $weather) {

// you can use the alternate syntactic sugar methods
// if you only need the deserializing mechanism once
$weather = Serializer::assign(Weather::class, $record);
$weather = Deserializer::assign(Weather::class, $record);

foreach (Serializer::assignAll(Weather::class, $collection, ['date', 'temperature', 'place']) as $weather) {
foreach (Deserializer::assignAll(Weather::class, $collection, ['date', 'temperature', 'place']) as $weather) {
// each $weather entry will be an instance of the Weather class;
}
```
Expand Down Expand Up @@ -113,15 +113,15 @@ enum Place
}
```

To get instances of your object, you now can call one of the `Serializer` method as show below:
To get instances of your object, you now can call one of the `Deserializer` method as show below:

```php
use League\Csv\Reader;
use League\Csv\Serializer
use League\Csv\Deserializer

$csv = Reader::createFromString($document);
$csv->setHeaderOffset(0);
$serializer = new Serializer(Weather::class, $csv->header());
$serializer = new Deserializer(Weather::class, $csv->header());

foreach ($csv as $record) {
$weather = $serializer->deserialize($record);
Expand All @@ -135,7 +135,7 @@ foreach ($serializer->deserializeAll($csv) as $weather) {

//or

foreach (Serializer::assignAll(Weather::class, $csv, $csv->getHeader()) as $weather) {
foreach (Deserializer::assignAll(Weather::class, $csv, $csv->getHeader()) as $weather) {
// each $weather entry will be an instance of the Weather class;
}
```
Expand Down Expand Up @@ -197,31 +197,31 @@ The above rule can be translated in plain english like this:
### Handling the empty string

Out of the box the `Serializer` makes no distinction between an empty string and the `null` value.
Out of the box the `Deserializer` makes no distinction between an empty string and the `null` value.
You can however change this behaviour using two (2) static methods:

- `Serializer::allowEmptyStringAsNull`
- `Serializer::disallowEmptyStringAsNull`
- `Deserializer::allowEmptyStringAsNull`
- `Deserializer::disallowEmptyStringAsNull`

When called these methods will change the class behaviour when it comes to handling empty string.
`Serializer::allowEmptyStringAsNull` will trigger conversion of all empty string into the `null` value
before typecasting whereas `Serializer::disallowEmptyStringAsNull` will maintain the distinction.
Using these methods will affect the `Serializer` usage throughout your codebase.
`Deserializer::allowEmptyStringAsNull` will trigger conversion of all empty string into the `null` value
before typecasting whereas `Deserializer::disallowEmptyStringAsNull` will maintain the distinction.
Using these methods will affect the `Deserializer` usage throughout your codebase.

```php
use League\Csv\Serializer;
use League\Csv\Deserializer;

$record = [
'date' => '2023-10-30',
'temperature' => '',
'place' => 'Berkeley',
];

$weather = Serializer::assign(Weather::class, $record);
$weather = Deserializer::assign(Weather::class, $record);
$weather->temperature; // returns null

Serializer::disallowEmptyStringAsNull();
Serializer::assign(Weather::class, $record);
Deserializer::disallowEmptyStringAsNull();
Deserializer::assign(Weather::class, $record);
//a TypeCastingFailed exception is thrown because we
//can not convert the empty string into a temperature property
//which expects `null` or a non-empty string.
Expand Down Expand Up @@ -330,7 +330,7 @@ use League\Csv\Serializer;
private array $data;
```

In the above example, the array has a JSON value associated with the key `data` and the `Serializer` will convert the
In the above example, the array has a JSON value associated with the key `data` and the `Deserializer` will convert the
JSON string into an `array` and use the `JSON_BIGINT_AS_STRING` option of the `json_decode` function.

If you use the array shape `list` or `csv` you can also typecast the `array` content using the
Expand All @@ -355,35 +355,50 @@ The `type` option only supports scalar type (`string`, `int`, `float` and `bool`

## Extending Type Casting capabilities

We provide two mechanisms to extends typecasting. You can register a closure via the `Serializer` class
We provide two mechanisms to extends typecasting. You can register a closure via the `Deserializer` class
or create a fully fledge `TypeCasting` class. Of course, the choice will depend on your use case.

### Registering a closure

You can register a closure using the `Serializer` class to convert a specific type. The type can be
You can register a closure using the `Deserializer` class to convert a specific type. The type can be
any built-in type or a specific class.

```php
use App\Domain\Money;
use League\Csv\Serializer;
use League\Csv\Deserializer;

Serializer::registerType(Money::class, fn (?string $value, bool $isNullable, ?int $default = null): Money => match (true) {
$isNullable && null === $value => Money::fromNaira($default ?? 20_00),
$typeCasting = fn (?string $value, bool $isNullable, int $default = 20_00): Money => match (true) {
$isNullable && null === $value => Money::fromNaira($default),
default => Money::fromNaira(filter_var($value, FILTER_VALIDATE_INT)),
});
};

Deserializer::registerType(Money::class, $typeCasting);
```

The Serializer will automatically call the closure for any `App\Domain\Money` conversion.
The `Deserializer` will automatically call the closure for any `App\Domain\Money` conversion. You can
also use the `Cell` attribute to further control the conversion

To do so, first, specify your casting with the attribute:

```php
use App\Domain\Money
use League\Csv\Serializer;

Serializer::registerType('int', fn (?string $value): int => 42);
#[Serializer\Cell(offset: 'amount', castArguments: ['default' => 20_00])]
private ?Money $naira;
```

<p class="message-notice">No need to specify the <code>cast</code> argument as the closure is registered.</p>

In the following example, the closure takes precedence over the `CastToInt` class to convert
to the `int` type. If you still wish to use the `CastToInt` class you are require to
explicitly declare it using the `Cell` attribute `cast` argument.
explicitly declare it via the `Cell` attribute `cast` argument.

```php
use League\Csv\Deserializer;

Deserializer::registerType('int', fn (?string $value): int => 42);
```

The closure signature is the following:

Expand All @@ -399,7 +414,7 @@ where:

To complete the feature you can use:

- `Serializer::unregisterType` to remove the registered closure for a specific `type`;
- `Deserializer::unregisterType` to remove the registered closure for a specific `type`;

The two (2) methods are static.

Expand All @@ -408,8 +423,8 @@ The two (2) methods are static.
### Implementing a TypeCasting class

If you need to support `Intersection` type, or you want to be able to fine tune the typecasting
you can provide your own class to typecast the value according to your own rules. To do so, first,
specify your casting with the attribute:
you can provide your own class to typecast the value according to your own rules. Since the class
is not registered by default you must configure its usage via the `Cell` attribute `cast` argument.

```php
use App\Domain\Money
Expand Down
Loading

0 comments on commit 23623fe

Please sign in to comment.