From 8f03a7e0cbe1b83434e14d05a3f533e26b7a243f Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Sun, 12 Feb 2023 18:12:06 +0700 Subject: [PATCH] fix: properly handle union of `null` and objects --- src/Mapper/Tree/Builder/UnionNodeBuilder.php | 4 ++ .../Object/UnionOfObjectsMappingTest.php | 46 +++++++++++++++---- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Mapper/Tree/Builder/UnionNodeBuilder.php b/src/Mapper/Tree/Builder/UnionNodeBuilder.php index a36b161f..bf6babda 100644 --- a/src/Mapper/Tree/Builder/UnionNodeBuilder.php +++ b/src/Mapper/Tree/Builder/UnionNodeBuilder.php @@ -83,6 +83,10 @@ private function tryToBuildClassNode(UnionType $type, Shell $shell, RootNodeBuil $classTypes = []; foreach ($type->types() as $subType) { + if ($subType instanceof NullType) { + continue; + } + if (! $subType instanceof ClassType) { return null; } diff --git a/tests/Integration/Mapping/Object/UnionOfObjectsMappingTest.php b/tests/Integration/Mapping/Object/UnionOfObjectsMappingTest.php index 16ef982f..0b53b718 100644 --- a/tests/Integration/Mapping/Object/UnionOfObjectsMappingTest.php +++ b/tests/Integration/Mapping/Object/UnionOfObjectsMappingTest.php @@ -19,9 +19,9 @@ public function test_objects_sharing_one_property_are_resolved_correctly(): void ->registerConstructor([SomeFooAndBarObject::class, 'constructorB']) ->mapper() ->map(UnionOfFooAndBarAndFoo::class, [ - 'foo', - ['foo' => 'foo', 'bar' => 'bar'], - ]); + 'foo', + ['foo' => 'foo', 'bar' => 'bar'], + ]); } catch (MappingError $error) { $this->mappingFail($error); } @@ -30,6 +30,27 @@ public function test_objects_sharing_one_property_are_resolved_correctly(): void self::assertInstanceOf(SomeFooAndBarObject::class, $result->objects[1]); } + public function test_mapping_to_union_of_null_and_objects_can_infer_object(): void + { + try { + $result = (new MapperBuilder()) + ->mapper() + ->map( + 'null|' . SomeObjectWithFooAndBar::class . '|' . SomeObjectWithBazAndFiz::class, + [ + 'baz' => 'baz', + 'fiz' => 'fiz', + ] + ); + } catch (MappingError $error) { + $this->mappingFail($error); + } + + self::assertInstanceOf(SomeObjectWithBazAndFiz::class, $result); + self::assertSame('baz', $result->baz); + self::assertSame('fiz', $result->fiz); + } + /** * * @dataProvider mapping_error_when_cannot_resolve_union_data_provider @@ -63,11 +84,6 @@ public function mapping_error_when_cannot_resolve_union_data_provider(): iterabl } } -final class NativeUnionOfObjects -{ - public SomeFooObject|SomeBarObject $object; -} - // PHP8.1 Readonly properties final class UnionOfFooAndBar { @@ -133,3 +149,17 @@ public static function constructorB(string $foo, string $bar): self return new self($foo, $bar, 'default baz'); } } + +final class SomeObjectWithFooAndBar +{ + public string $foo; + + public string $bar; +} + +final class SomeObjectWithBazAndFiz +{ + public string $baz; + + public string $fiz; +}