Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -21,56 +38,30 @@ 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;
}

if (\is_object($value) && \method_exists($value, '__toString')) {
return $value->__toString();
}

if (\is_array($value) || \is_object($value)) {
return '';
}

// 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
XMLElementEntry,
XMLEntry};

final readonly class ExcelRowsNormalizer
final readonly class EntryNormalizer
{
public function __construct(
private string $dateFormat = 'Y-m-d',
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -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']))
Expand All @@ -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())))
Expand All @@ -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())))
Expand All @@ -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(
Expand All @@ -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))
Expand All @@ -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()])))
Expand All @@ -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))
Expand All @@ -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', '<root><item>value</item></root>'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -24,18 +41,24 @@ 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),
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/etl/src/Flow/ETL/Row/Entry/EnumEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ public function toString() : string
return '';
}

if ($this->value instanceof \BackedEnum) {
return (string) $this->value->value;
}

Comment on lines +98 to +101
Copy link
Member Author

@stloyd stloyd Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing that, in theory, may be considered a BC break, but in practice, IMO, it makes sense to return a value for backed ones, not a name (the adapter does that already most of the time...).

return $this->value->name;
}

Expand Down
Loading