From 96662e7b2a8f1192ad0207a01eaae7137e8d1f54 Mon Sep 17 00:00:00 2001 From: Jakub Kulhan Date: Fri, 31 May 2024 13:30:16 +0200 Subject: [PATCH] fix value converter deep nested objects --- .../src/Converter/DefaultValueConverter.php | 54 +++++++++++-------- .../Converter/DefaultValueConverterTest.php | 7 +++ .../Converter/Fixture/DeepNestedObject.php | 14 +++++ 3 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 data-access-kit/test/Converter/Fixture/DeepNestedObject.php diff --git a/data-access-kit/src/Converter/DefaultValueConverter.php b/data-access-kit/src/Converter/DefaultValueConverter.php index fb20fec..9a37915 100644 --- a/data-access-kit/src/Converter/DefaultValueConverter.php +++ b/data-access-kit/src/Converter/DefaultValueConverter.php @@ -31,7 +31,7 @@ public function __construct( { } - public function objectToDatabase(Table $table, Column $column, mixed $value): mixed + public function objectToDatabase(Table $table, Column $column, mixed $value, bool $encode = true): mixed { if ($value === null) { return null; @@ -49,12 +49,12 @@ public function objectToDatabase(Table $table, Column $column, mixed $value): mi $valueType = $column->reflection->getType(); if ($valueType instanceof ReflectionNamedType) { if (in_array($valueType->getName(), ["int", "float", "string", "bool"], true)) { - // passthrough + $returnValue = $value; } else if ($valueType->getName() === "object") { - $value = json_encode($value); + $returnValue = $encode ? json_encode($value) : $value; } else if ($valueType->getName() === "array") { if ($column->itemType === null) { - $value = json_encode($value); + $returnValue = $encode ? json_encode($value) : $value; } else { $nestedTable = $this->registry->get($column->itemType); $jsonArray = []; @@ -65,14 +65,15 @@ public function objectToDatabase(Table $table, Column $column, mixed $value): mi $nestedTable, $nestedColumn, $nestedColumn->reflection->getValue($item), + false, ); } } - $value = json_encode($jsonArray); + $returnValue = $encode ? json_encode($jsonArray) : $jsonArray; } } else if (in_array($valueType->getName(), [DateTime::class, DateTimeImmutable::class], true)) { /** @var DateTime|DateTimeImmutable $value */ - $value = (clone $value)->setTimezone($this->dateTimeZone)->format($this->dateTimeFormat); + $returnValue = (clone $value)->setTimezone($this->dateTimeZone)->format($this->dateTimeFormat); } else if (null !== ($nestedTable = $this->registry->maybeGet($valueType->getName()))) { $jsonObject = new stdClass(); foreach ($nestedTable->columns as $nestedColumn) { @@ -80,9 +81,10 @@ public function objectToDatabase(Table $table, Column $column, mixed $value): mi $nestedTable, $nestedColumn, $nestedColumn->reflection->getValue($value), + false, ); } - $value = json_encode($jsonObject); + $returnValue = $encode ? json_encode($jsonObject) : $jsonObject; } else { throw new ConverterException(sprintf( "Unsupported type [%s] of property [%s::\$%s].", @@ -99,7 +101,7 @@ public function objectToDatabase(Table $table, Column $column, mixed $value): mi )); } - return $value; + return $returnValue; } finally { if ($recursionGuardKey !== null) { @@ -108,7 +110,7 @@ public function objectToDatabase(Table $table, Column $column, mixed $value): mi } } - public function databaseToObject(Table $table, Column $column, mixed $value): mixed + public function databaseToObject(Table $table, Column $column, mixed $value, bool $decode = true): mixed { if ($value === null) { return null; @@ -117,41 +119,51 @@ public function databaseToObject(Table $table, Column $column, mixed $value): mi $valueType = $column->reflection->getType(); if ($valueType instanceof ReflectionNamedType) { if (in_array($valueType->getName(), ["int", "float", "string", "bool"], true)) { - // passthrough + $returnValue = $value; } else if ($valueType->getName() === "object") { - $value = json_decode($value); + $returnValue = $decode ? json_decode($value) : $value; } else if ($valueType->getName() === "array") { if ($column->itemType === null) { - $value = json_decode($value); + $returnValue = $decode ? json_decode($value) : $value; } else { $nestedTable = $this->registry->get($column->itemType); $array = []; - foreach (json_decode($value) as $jsonObject) { + foreach ($decode ? json_decode($value) : $value as $jsonObject) { $nestedObject = $nestedTable->reflection->newInstanceWithoutConstructor(); foreach ($nestedTable->columns as $nestedColumn) { $nestedColumn->reflection->setValue( $nestedObject, - $this->databaseToObject($nestedTable, $nestedColumn, $jsonObject->{$nestedColumn->name}), + $this->databaseToObject( + $nestedTable, + $nestedColumn, + $jsonObject->{$nestedColumn->name}, + false, + ), ); } $array[] = $nestedObject; } - $value = $array; + $returnValue = $array; } } else if ($valueType->getName() === DateTime::class) { - $value = DateTime::createFromFormat($this->dateTimeFormat, $value, $this->dateTimeZone); + $returnValue = DateTime::createFromFormat($this->dateTimeFormat, $value, $this->dateTimeZone); } else if ($valueType->getName() === DateTimeImmutable::class) { - $value = DateTimeImmutable::createFromFormat($this->dateTimeFormat, $value, $this->dateTimeZone); + $returnValue = DateTimeImmutable::createFromFormat($this->dateTimeFormat, $value, $this->dateTimeZone); } else if (null !== ($nestedTable = $this->registry->maybeGet($valueType->getName()))) { - $jsonObject = json_decode($value); + $jsonObject = $decode ? json_decode($value) : $value; $nestedObject = $nestedTable->reflection->newInstanceWithoutConstructor(); foreach ($nestedTable->columns as $nestedColumn) { $nestedColumn->reflection->setValue( $nestedObject, - $this->databaseToObject($nestedTable, $nestedColumn, $jsonObject->{$nestedColumn->name}), + $this->databaseToObject( + $nestedTable, + $nestedColumn, + $jsonObject->{$nestedColumn->name}, + false, + ), ); } - $value = $nestedObject; + $returnValue = $nestedObject; } else { throw new ConverterException(sprintf( "Unsupported type [%s] of property [%s::\$%s].", @@ -168,6 +180,6 @@ public function databaseToObject(Table $table, Column $column, mixed $value): mi )); } - return $value; + return $returnValue; } } diff --git a/data-access-kit/test/Converter/DefaultValueConverterTest.php b/data-access-kit/test/Converter/DefaultValueConverterTest.php index 05cabc3..6e3e0c1 100644 --- a/data-access-kit/test/Converter/DefaultValueConverterTest.php +++ b/data-access-kit/test/Converter/DefaultValueConverterTest.php @@ -4,6 +4,7 @@ use DataAccessKit\Attribute\Column; use DataAccessKit\Attribute\Table; +use DataAccessKit\Converter\Fixture\DeepNestedObject; use DataAccessKit\Converter\Fixture\NestedObject; use DataAccessKit\Registry; use DateTime; @@ -96,6 +97,9 @@ public static function data() /** @var NestedObject[] */ #[Column] public array $nestedArrayDoc; /** @var NestedObject[]|null */ #[Column] public ?array $nullableNestedArrayDocNull; /** @var NestedObject[]|null */ #[Column] public ?array $nullableNestedArrayDocNotNull; + #[Column] public DeepNestedObject $deepNestedObject; + #[Column] public ?DeepNestedObject $nullableDeepNestedObjectNull; + #[Column] public ?DeepNestedObject $nullableDeepNestedObjectNotNull; }); $data = [ @@ -132,6 +136,9 @@ public static function data() "nestedArrayDoc" => [[new NestedObject("value1"), new NestedObject("value2")], '[{"key":"value1"},{"key":"value2"}]'], "nullableNestedArrayDocNull" => [null, null], "nullableNestedArrayDocNotNull" => [[new NestedObject("value1"), new NestedObject("value2")], '[{"key":"value1"},{"key":"value2"}]'], + "deepNestedObject" => [new DeepNestedObject(new DeepNestedObject()), '{"nested":{"nested":null}}'], + "nullableDeepNestedObjectNull" => [null, null], + "nullableDeepNestedObjectNotNull" => [new DeepNestedObject(new DeepNestedObject()), '{"nested":{"nested":null}}'], ]; foreach ($data as $columnName => [$objectValue, $databaseValue]) { yield $columnName => [$table, $table->columns[$columnName], $objectValue, $databaseValue]; diff --git a/data-access-kit/test/Converter/Fixture/DeepNestedObject.php b/data-access-kit/test/Converter/Fixture/DeepNestedObject.php new file mode 100644 index 0000000..f12b00c --- /dev/null +++ b/data-access-kit/test/Converter/Fixture/DeepNestedObject.php @@ -0,0 +1,14 @@ +