From 6851c1d5aed3d6612d3d238554eb61214d878ce3 Mon Sep 17 00:00:00 2001 From: ignace nyamagana butera Date: Wed, 9 Oct 2024 05:31:06 +0200 Subject: [PATCH] Improve documentation --- docs/9.0/converter/json.md | 22 ++++---- docs/9.0/reader/record-mapping.md | 88 ++++++++++++++++------------- src/Serializer/Denormalizer.php | 14 ++++- src/Serializer/DenormalizerTest.php | 4 +- 4 files changed, 74 insertions(+), 54 deletions(-) diff --git a/docs/9.0/converter/json.md b/docs/9.0/converter/json.md index 1513b549..0803e086 100644 --- a/docs/9.0/converter/json.md +++ b/docs/9.0/converter/json.md @@ -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.

Because we are building a JSON structure, the JsonConverter object throws generic SPL Exception instead of League\Csv\Exception.

+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 @@ -43,11 +45,11 @@ $converter = JsonConverter::create() ->withoutHexQuot(); ``` -

Because we are converting one record at a time, the class always uses JSON_THROW_ON_ERROR -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 ALWAYS set.

+

The class always uses the JSON_THROW_ON_ERROR 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 ALWAYS set.

-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 diff --git a/docs/9.0/reader/record-mapping.md b/docs/9.0/reader/record-mapping.md index fa2c0719..62ffd01e 100644 --- a/docs/9.0/reader/record-mapping.md +++ b/docs/9.0/reader/record-mapping.md @@ -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.

The mechanism relies on PHP's Reflection feature. It does not use the class constructor to perform the conversion. @@ -41,7 +42,7 @@ the mechanism may either fail or produce unexpected results.

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: @@ -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; } @@ -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: @@ -130,8 +134,6 @@ PHP attributes:

The MapRecord attribute is added in version 9.17.0

Before version 9.17.0 the cell value must be a string or null. Starting with version 9.17.0, this limitation has been lifted.

-### Improving field mapping - Here's an example of how the `League\Csv\Serializer\MapCell` attribute works: ```php @@ -230,7 +232,43 @@ $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 + +

Using the global state is no longer recommended. This feature is +deprecated and will be removed in the next major release.

+ +This system rely on two (2) static methods: - `League\Csv\Serializer\Denormalizer::allowEmptyStringAsNull` - `League\Csv\Serializer\Denormalizer::disallowEmptyStringAsNull` @@ -238,7 +276,8 @@ You can however change this behaviour using two (2) static methods: 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; @@ -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

The feature is available since version 9.13.0

diff --git a/src/Serializer/Denormalizer.php b/src/Serializer/Denormalizer.php index 4c3c9703..8b9d6798 100644 --- a/src/Serializer/Denormalizer.php +++ b/src/Serializer/Denormalizer.php @@ -58,7 +58,12 @@ 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 { @@ -66,7 +71,12 @@ public static function allowEmptyStringAsNull(): void } /** - * 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 { diff --git a/src/Serializer/DenormalizerTest.php b/src/Serializer/DenormalizerTest.php index 85a2a5a9..b0fb4db6 100644 --- a/src/Serializer/DenormalizerTest.php +++ b/src/Serializer/DenormalizerTest.php @@ -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); }