Skip to content

Commit

Permalink
Improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Oct 9, 2024
1 parent d037369 commit 6851c1d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 54 deletions.
22 changes: 12 additions & 10 deletions docs/9.0/converter/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ title: Converting a CSV into a JSON

# JSON conversion

The `JsonConverter` converts or store a CSV records collection into a JSON structure.

## Settings

Prior to converting your records collection into a JSON structure, you may wish to configure
the converter.
The `JsonConverter` converts or store a collection into a JSON structure.

<p class="message-warning">Because we are building a <code>JSON</code> structure, the <code>JsonConverter</code> object
throws generic <code>SPL Exception</code> instead of <code>League\Csv\Exception</code>.</p>

To reduce memory usage, the converter transform one record at a time. This means that the class object settings are
geared toward a single element and not the whole claass,

## Settings

Prior to converting your collection into a JSON structure, you may wish to configure it.

### JSON encode flags

```php
Expand Down Expand Up @@ -43,11 +45,11 @@ $converter = JsonConverter::create()
->withoutHexQuot();
```

<p class="message-notice">Because we are converting one record at a time, the class always uses <code>JSON_THROW_ON_ERROR</code>
to stop the collection conversion. As such adding or removing the flag using the methods describe here before will
have no effect on its usage, the flag is <strong>ALWAYS</strong> set.</p>
<p class="message-notice">The class always uses the <code>JSON_THROW_ON_ERROR</code> to enable stop the collection
conversion in case of an error. That's why adding or removing the flag using the methods will have no effect on its
usage, the flag is <strong>ALWAYS</strong> set.</p>

At any given time you can check which flags is being used via the `JsonConverter::useFlags` method. As for the other method
To quickly check which flags is being used, calle the `JsonConverter::useFlags` method. As for the other methods
a more expressive way exists.

```php
Expand Down
88 changes: 48 additions & 40 deletions docs/9.0/reader/record-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ In the following sections we will explain the process and how you can control it

## Prerequisite

The deserialization process is done in two steps. The first step is decoding your CSV into
a collection of records. This part is already handle by the package. The second step is a
denormalization process which we will focuse on. The process is geared toward converting
record into DTO or objects without complex logic in their constructors.
The deserialization process is done in two steps. The first step is decoding your CSV record into
a PHP `array`, this part is already handle by the package. The second step is a denormalization
process which will convert your `array` into an object. This is the part we will focus on.
The process is geared toward converting records into DTO or objects without complex
logic in their constructors.

<p class="message-notice">The mechanism relies on PHP's <code>Reflection</code>
feature. It does not use the class constructor to perform the conversion.
Expand All @@ -41,7 +42,7 @@ the mechanism may either fail or produce unexpected results.</p>

To work as intended the mechanism expects the following:

- A target class where the array will be denormalized in;
- A target class where the `array` will be denormalized in;
- information on how to convert cell values into object properties;

As an example throughout the documentation we will assume the following CSV document:
Expand Down Expand Up @@ -96,6 +97,7 @@ an `Iterator` containing only instances of your specified class.
use League\Csv\Reader;

$csv = Reader::createFromString($document);
/** @var ClimaticRecord $instance */
foreach ($csv->getRecordsAsObject(ClimaticRecord::class) as $instance) {
// each $instance entry will be an instance of the ClimaticRecord class;
}
Expand All @@ -119,6 +121,8 @@ The autodiscovery feature works out of the box with public properties or argumen

the `nullable` aspect of the property is also automatically handled.

### Improving field mapping

If the autodiscovery feature is not enough, you can complete the conversion information using the following
PHP attributes:

Expand All @@ -130,8 +134,6 @@ PHP attributes:
<p class="message-info">The <code>MapRecord</code> attribute is added in version <code>9.17.0</code></p>
<p class="message-notice">Before version <code>9.17.0</code> the cell value must be a <code>string</code> or <code>null</code>. Starting with <code>version 9.17.0</code>, this limitation has been lifted.</p>

### Improving field mapping

Here's an example of how the `League\Csv\Serializer\MapCell` attribute works:

```php
Expand Down Expand Up @@ -230,15 +232,52 @@ $item->description = ' je suis trop fort'; // the white space is preserved
### Handling the empty string

Out of the box the mechanism converts any empty string value into the `null` value.
You can however change this behaviour using two (2) static methods:

##### Using Attributes

Starting with version `9.17.0` a granular and robust system is introduced. It is now the recommended
way to handle empty string conversion. When in used the new feature override the now deprecated
global state mechanism. You can control the conversion at the field or at the record level.

At the field level you need to use newly introduced `convertEmptyStringToNull` argument.

When the value is set to `true`, the conversion will happen. If set to `false`, no conversion will take
place. By default, the value is set to `null` to defer the behaviour settings at the object level.

At the object level you can use the new `MapRecord` attribute with the same argument and the same
possible values. If the value is set to `null` the behaviour will fall back to the global behaviour to
avoid BC break.

```php
#[Serializer\MapRecord(convertEmptyStringToNull: true)]
final readonly class Car
{
public function __construct(
private Wheel $wheel,
#[Serializer\MapCell(convertEmptyStringToNull: false)]
private Driver $driver
) {}
}
```

In the above example, every property will see the empty string being converted to `null` except
for the `$driver` property.

#### Using global state

<p class="message-warning">Using the global state is no longer recommended. This feature is
deprecated and will be removed in the next major release.</p>

This system rely on two (2) static methods:

- `League\Csv\Serializer\Denormalizer::allowEmptyStringAsNull`
- `League\Csv\Serializer\Denormalizer::disallowEmptyStringAsNull`

When called these methods will change the behaviour when it comes to handling empty string.
`Denormalizer::allowEmptyStringAsNull` will convert any empty string into the `null` value
before typecasting whereas `Denormalizer::disallowEmptyStringAsNull` will preserve the value.
Using these methods will affect the results of the process throughout your codebase.

**Using these methods will affect the results of all conversion throughout your codebase.**

```php
use League\Csv\Reader;
Expand All @@ -262,37 +301,6 @@ foreach ($csv->getRecordsAsObject(ClimaticRecord::class) {
}
```

Starting with version `9.17.0` you can get a more granular effect and override the global setting. You can either
control the conversion at the field level or at the object level.

At the field level you need to use newly introduced `convertEmptyStringToNull` argument as shown in the example below:

```php
#[Serializer\MapCell(convertEmptyStringToNull: true)]
private DateTimeImmutable $observedOn;
```

When the value is set to `true`, the conversion will happen. If set to `false`, no conversion will take place.
By default, the value is set to `null` to defer the behaviour settings at the object level.

At the object level you can use the new `MapRecord` attribute with the same argument.

```php
#[Serializer\MapRecord(convertEmptyStringToNull: true)]
final readonly class Car
{
public function __construct(
private Wheel $wheel,
private Driver $driver
) {
}
}
```

Just like with the `MapCell` attribute, if set to `true` the conversion will happen for **all fields**, but if set
to `false` no conversion will take place. By default, it is set to `null` and the conversion behaviour falls back
to the global behaviour.

### Post Mapping

<p class="message-info">The feature is available since version <code>9.13.0</code></p>
Expand Down
14 changes: 12 additions & 2 deletions src/Serializer/Denormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,25 @@ public function __construct(string $className, array $propertyNames = [])
}

/**
* Enable converting empty string to the null value.
* @deprecated since version 9.17.0
*
* @see MapRecord::$convertEmptyStringToNull
* @see MapCell::$convertEmptyStringToNull
*
* Enables converting empty string to the null value.
*/
public static function allowEmptyStringAsNull(): void
{
self::$convertEmptyStringToNull = true;
}

/**
* Disable converting empty string to the null value.
* @deprecated since version 9.17.0
*
* @see MapRecord::$convertEmptyStringToNull
* @see MapCell::$convertEmptyStringToNull
*
* Disables converting empty string to the null value.
*/
public static function disallowEmptyStringAsNull(): void
{
Expand Down
4 changes: 2 additions & 2 deletions src/Serializer/DenormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -488,11 +488,11 @@ public function testEmptyStringHandling(): void
public ?string $foo;
};

Denormalizer::disallowEmptyStringAsNull();
Denormalizer::disallowEmptyStringAsNull(); /* @phpstan-ignore-line */

self::assertSame('', Denormalizer::assign($foobar::class, $record)->foo); /* @phpstan-ignore-line */

Denormalizer::allowEmptyStringAsNull();
Denormalizer::allowEmptyStringAsNull(); /* @phpstan-ignore-line */

self::assertNull(Denormalizer::assign($foobar::class, $record)->foo);
}
Expand Down

0 comments on commit 6851c1d

Please sign in to comment.