From b17672f9811dff572ec82e770946d8803c4d9857 Mon Sep 17 00:00:00 2001 From: Joseph Bielawski Date: Fri, 26 Dec 2025 18:51:36 +0100 Subject: [PATCH 1/3] Unify adapter entry normalizers --- .../CSV/RowsNormalizer/EntryNormalizer.php | 84 ++++++++++--------- .../RowsNormalizer/ExcelRowsNormalizer.php | 13 +-- .../JSON/RowsNormalizer/EntryNormalizer.php | 46 ++++++++-- .../etl/src/Flow/ETL/Row/Entry/EnumEntry.php | 4 + 4 files changed, 88 insertions(+), 59 deletions(-) diff --git a/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php index c86282c98..420541683 100644 --- a/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php @@ -5,8 +5,25 @@ namespace Flow\ETL\Adapter\CSV\RowsNormalizer; use function Flow\ETL\DSL\date_interval_to_microseconds; +use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Row\Entry; -use Flow\ETL\Row\Entry\{DateEntry, DateTimeEntry, EnumEntry, JsonEntry, ListEntry, MapEntry, StructureEntry, TimeEntry, UuidEntry, XMLElementEntry, XMLEntry}; +use Flow\ETL\Row\Entry\{BooleanEntry, + DateEntry, + DateTimeEntry, + EnumEntry, + FloatEntry, + HTMLElementEntry, + HTMLEntry, + IntegerEntry, + JsonEntry, + ListEntry, + MapEntry, + StringEntry, + StructureEntry, + TimeEntry, + UuidEntry, + XMLElementEntry, + XMLEntry}; final readonly class EntryNormalizer { @@ -21,56 +38,41 @@ public function __construct( */ public function normalize(Entry $entry) : string|float|int|bool|null { - $value = match ($entry::class) { - UuidEntry::class, - XMLElementEntry::class, - XMLEntry::class => $entry->toString(), + return match ($entry::class) { + BooleanEntry::class, + IntegerEntry::class, + FloatEntry::class, + StringEntry::class => $entry->value(), DateTimeEntry::class => $entry->value()?->format($this->dateTimeFormat), DateEntry::class => $entry->value()?->format($this->dateFormat), TimeEntry::class => $entry->value() ? date_interval_to_microseconds($entry->value()) : null, - EnumEntry::class => $entry->value()?->name, + JsonEntry::class, ListEntry::class, MapEntry::class, - StructureEntry::class => \json_encode($entry->value(), \JSON_THROW_ON_ERROR), - JsonEntry::class => $entry->toString(), - default => $entry->value(), + StructureEntry::class => $this->normalizeToJson($entry->value()), + EnumEntry::class, + UuidEntry::class, + XMLEntry::class, + XMLElementEntry::class, + HTMLEntry::class, + HTMLElementEntry::class => $entry->toString(), + default => throw new InvalidArgumentException('Unknown entry type: ' . $entry::class), }; + } - // Ensure we return only the expected types - if (\is_string($value)) { - return $value; - } - - if (\is_float($value)) { - return $value; - } - - if (\is_int($value)) { - return $value; - } - - if (\is_bool($value)) { - return $value; - } - - if ($value === null) { - return null; - } - - // Handle remaining types - if (\is_resource($value)) { - return (string) $value; - } + private function normalizeEnumEntry(EnumEntry $entry) : ?string + { + $value = $entry->value(); - if (\is_object($value) && \method_exists($value, '__toString')) { - return $value->__toString(); + if ($value instanceof \BackedEnum) { + return (string) $value->value; } - if (\is_array($value) || \is_object($value)) { - return ''; - } + return $value?->name; + } - // At this point, we should have covered all cases - return ''; + private function normalizeToJson(mixed $value) : ?string + { + return $value !== null ? \json_encode($value, JSON_THROW_ON_ERROR) : null; } } diff --git a/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php b/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php index bc7add897..33fe06661 100644 --- a/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php +++ b/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php @@ -75,11 +75,11 @@ private function normalizeEntry(Entry $entry) : bool|float|int|string|null DateTimeEntry::class => $entry->value()?->format($this->dateTimeFormat), DateEntry::class => $entry->value()?->format($this->dateFormat), TimeEntry::class => $entry->value()?->format($this->timeFormat), - EnumEntry::class => $this->normalizeEnumEntry($entry), JsonEntry::class, ListEntry::class, MapEntry::class, StructureEntry::class => $this->normalizeToJson($entry->value()), + EnumEntry::class, UuidEntry::class, XMLEntry::class, XMLElementEntry::class, @@ -89,17 +89,6 @@ private function normalizeEntry(Entry $entry) : bool|float|int|string|null }; } - private function normalizeEnumEntry(EnumEntry $entry) : ?string - { - $value = $entry->value(); - - if ($value instanceof \BackedEnum) { - return (string) $value->value; - } - - return $value?->name; - } - private function normalizeToJson(mixed $value) : ?string { return $value !== null ? \json_encode($value, JSON_THROW_ON_ERROR) : null; diff --git a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php index d8afd396a..4401668fd 100644 --- a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php @@ -5,8 +5,25 @@ namespace Flow\ETL\Adapter\JSON\RowsNormalizer; use function Flow\ETL\DSL\date_interval_to_microseconds; +use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Row\Entry; -use Flow\ETL\Row\Entry\{DateEntry, DateTimeEntry, EnumEntry, JsonEntry, ListEntry, MapEntry, StructureEntry, TimeEntry, UuidEntry, XMLElementEntry, XMLEntry}; +use Flow\ETL\Row\Entry\{BooleanEntry, + DateEntry, + DateTimeEntry, + EnumEntry, + FloatEntry, + HTMLElementEntry, + HTMLEntry, + IntegerEntry, + JsonEntry, + ListEntry, + MapEntry, + StringEntry, + StructureEntry, + TimeEntry, + UuidEntry, + XMLElementEntry, + XMLEntry}; final readonly class EntryNormalizer { @@ -24,21 +41,38 @@ public function __construct( public function normalize(Entry $entry) : string|float|int|bool|array|null { return match ($entry::class) { - UuidEntry::class => $entry->toString(), + BooleanEntry::class, + IntegerEntry::class, + FloatEntry::class, + StringEntry::class => $entry->value(), DateTimeEntry::class => $entry->value()?->format($this->dateTimeFormat), DateEntry::class => $entry->value()?->format($this->dateFormat), TimeEntry::class => $entry->value() ? date_interval_to_microseconds($entry->value()) : null, - EnumEntry::class => $entry->value()?->name, JsonEntry::class => $this->normalizeJsonValue($entry->value()?->toArray()), + EnumEntry::class, ListEntry::class, MapEntry::class, StructureEntry::class, - XMLElementEntry::class => $entry->toString(), - XMLEntry::class => $entry->toString(), - default => $this->normalizeValue($entry->value()), + UuidEntry::class, + XMLEntry::class, + XMLElementEntry::class, + HTMLEntry::class, + HTMLElementEntry::class => $entry->toString(), + default => throw new InvalidArgumentException('Unknown entry type: ' . $entry::class), }; } + private function normalizeEnumEntry(EnumEntry $entry) : ?string + { + $value = $entry->value(); + + if ($value instanceof \BackedEnum) { + return (string) $value->value; + } + + return $value?->name; + } + /** * @return null|array|bool|float|int|string */ diff --git a/src/core/etl/src/Flow/ETL/Row/Entry/EnumEntry.php b/src/core/etl/src/Flow/ETL/Row/Entry/EnumEntry.php index 18ee6b205..e683cfa08 100644 --- a/src/core/etl/src/Flow/ETL/Row/Entry/EnumEntry.php +++ b/src/core/etl/src/Flow/ETL/Row/Entry/EnumEntry.php @@ -95,6 +95,10 @@ public function toString() : string return ''; } + if ($this->value instanceof \BackedEnum) { + return (string) $this->value->value; + } + return $this->value->name; } From 54da1457333eedec88593ad68455b9390f636cee Mon Sep 17 00:00:00 2001 From: Joseph Bielawski Date: Fri, 26 Dec 2025 18:52:41 +0100 Subject: [PATCH 2/3] Rename `ExcelRowsNormalizer` to `EntryNormalizer` to match other adapters --- .../Flow/ETL/Adapter/Excel/ExcelLoader.php | 4 +-- ...RowsNormalizer.php => EntryNormalizer.php} | 2 +- ...alizerTest.php => EntryNormalizerTest.php} | 28 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) rename src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/{ExcelRowsNormalizer.php => EntryNormalizer.php} (98%) rename src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/{ExcelRowsNormalizerTest.php => EntryNormalizerTest.php} (84%) diff --git a/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/ExcelLoader.php b/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/ExcelLoader.php index 15a4efefe..46996a867 100644 --- a/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/ExcelLoader.php +++ b/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/ExcelLoader.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Adapter\Excel; -use Flow\ETL\Adapter\Excel\RowsNormalizer\ExcelRowsNormalizer; +use Flow\ETL\Adapter\Excel\RowsNormalizer\EntryNormalizer; use Flow\ETL\Adapter\Excel\Sheet\SheetNameAssertion; use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\{FlowContext, Loader, Row, Rows}; @@ -68,7 +68,7 @@ public function destination() : Path public function load(Rows $rows, FlowContext $context) : void { - $normalizer = new ExcelRowsNormalizer( + $normalizer = new EntryNormalizer( dateFormat: $this->dateFormat, dateTimeFormat: $this->dateTimeFormat, timeFormat: $this->timeFormat, diff --git a/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php b/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/EntryNormalizer.php similarity index 98% rename from src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php rename to src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/EntryNormalizer.php index 33fe06661..ab1c08584 100644 --- a/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/ExcelRowsNormalizer.php +++ b/src/adapter/etl-adapter-excel/src/Flow/ETL/Adapter/Excel/RowsNormalizer/EntryNormalizer.php @@ -25,7 +25,7 @@ XMLElementEntry, XMLEntry}; -final readonly class ExcelRowsNormalizer +final readonly class EntryNormalizer { public function __construct( private string $dateFormat = 'Y-m-d', diff --git a/src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/ExcelRowsNormalizerTest.php b/src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/EntryNormalizerTest.php similarity index 84% rename from src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/ExcelRowsNormalizerTest.php rename to src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/EntryNormalizerTest.php index 9d04f363c..5618fc9b2 100644 --- a/src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/ExcelRowsNormalizerTest.php +++ b/src/adapter/etl-adapter-excel/tests/Flow/ETL/Adapter/Excel/Tests/Unit/RowsNormalizer/EntryNormalizerTest.php @@ -6,7 +6,7 @@ use function Flow\ETL\DSL\{enum_entry, float_entry, int_entry, json_entry, list_entry, map_entry, row, string_entry, structure_entry, xml_entry}; use function Flow\Types\DSL\{type_integer, type_list, type_map, type_string, type_structure}; -use Flow\ETL\Adapter\Excel\RowsNormalizer\ExcelRowsNormalizer; +use Flow\ETL\Adapter\Excel\RowsNormalizer\EntryNormalizer; use Flow\ETL\Tests\FlowTestCase; enum BackedStringTestEnum : string @@ -27,11 +27,11 @@ enum UnitTestEnum case PENDING; } -final class ExcelRowsNormalizerTest extends FlowTestCase +final class EntryNormalizerTest extends FlowTestCase { public function test_headers() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $headers = $normalizer->headers( row( @@ -46,7 +46,7 @@ public function test_headers() : void public function test_normalize_backed_int_enum() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(enum_entry('count', BackedIntTestEnum::TWO)) @@ -57,7 +57,7 @@ public function test_normalize_backed_int_enum() : void public function test_normalize_backed_string_enum() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(enum_entry('status', BackedStringTestEnum::ACTIVE)) @@ -68,7 +68,7 @@ public function test_normalize_backed_string_enum() : void public function test_normalize_json_entry_with_null_value() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(json_entry('data', null)) @@ -79,7 +79,7 @@ public function test_normalize_json_entry_with_null_value() : void public function test_normalize_json_entry_with_value() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(json_entry('data', ['key' => 'value'])) @@ -90,7 +90,7 @@ public function test_normalize_json_entry_with_value() : void public function test_normalize_list_entry() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(list_entry('items', [1, 2, 3], type_list(type_integer()))) @@ -101,7 +101,7 @@ public function test_normalize_list_entry() : void public function test_normalize_map_entry() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(map_entry('mapping', ['a' => 1, 'b' => 2], type_map(type_string(), type_integer()))) @@ -112,7 +112,7 @@ public function test_normalize_map_entry() : void public function test_normalize_mixed_row() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row( @@ -128,7 +128,7 @@ enum_entry('status', BackedStringTestEnum::ACTIVE) public function test_normalize_null_enum_entry() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(enum_entry('status', null)) @@ -139,7 +139,7 @@ public function test_normalize_null_enum_entry() : void public function test_normalize_structure_entry() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(structure_entry('person', ['name' => 'John', 'age' => 30], type_structure(['name' => type_string(), 'age' => type_integer()]))) @@ -150,7 +150,7 @@ public function test_normalize_structure_entry() : void public function test_normalize_unit_enum() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(enum_entry('status', UnitTestEnum::PENDING)) @@ -161,7 +161,7 @@ public function test_normalize_unit_enum() : void public function test_normalize_xml_entry() : void { - $normalizer = new ExcelRowsNormalizer(); + $normalizer = new EntryNormalizer(); $result = $normalizer->normalize( row(xml_entry('xml', 'value')) From 7a72f86837fedaf76a251b0576530c06d2cb6298 Mon Sep 17 00:00:00 2001 From: Joseph Bielawski Date: Fri, 26 Dec 2025 18:54:43 +0100 Subject: [PATCH 3/3] Remove dead code --- .../Adapter/CSV/RowsNormalizer/EntryNormalizer.php | 11 ----------- .../Adapter/JSON/RowsNormalizer/EntryNormalizer.php | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php index 420541683..84dd069cf 100644 --- a/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php @@ -60,17 +60,6 @@ public function normalize(Entry $entry) : string|float|int|bool|null }; } - private function normalizeEnumEntry(EnumEntry $entry) : ?string - { - $value = $entry->value(); - - if ($value instanceof \BackedEnum) { - return (string) $value->value; - } - - return $value?->name; - } - private function normalizeToJson(mixed $value) : ?string { return $value !== null ? \json_encode($value, JSON_THROW_ON_ERROR) : null; diff --git a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php index 4401668fd..dda9d744e 100644 --- a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php @@ -62,17 +62,6 @@ public function normalize(Entry $entry) : string|float|int|bool|array|null }; } - private function normalizeEnumEntry(EnumEntry $entry) : ?string - { - $value = $entry->value(); - - if ($value instanceof \BackedEnum) { - return (string) $value->value; - } - - return $value?->name; - } - /** * @return null|array|bool|float|int|string */