From f43f5e1458fa457914fcf9f09441bdaf597c87db Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Wed, 21 Feb 2024 23:20:00 +0700 Subject: [PATCH] feat: null to `Traversable` or `ArrayAccess` is now handled & returns empty. --- CHANGELOG.md | 1 + psalm.xml | 2 ++ .../TraversableToArrayAccessTransformer.php | 5 ++++ .../TraversableToTraversableTransformer.php | 5 ++++ .../ObjectWithNullCollectionProperty.php | 25 +++++++++++++++++++ ...bjectWithNotNullArrayAccessPropertyDto.php | 24 ++++++++++++++++++ ...bjectWithNotNullTraversablePropertyDto.php | 24 ++++++++++++++++++ .../TraversableToArrayAccessMappingTest.php | 16 ++++++++++++ .../TraversableToTraversableMappingTest.php | 13 ++++++++++ 9 files changed, 115 insertions(+) create mode 100644 tests/Fixtures/ArrayLike/ObjectWithNullCollectionProperty.php create mode 100644 tests/Fixtures/ArrayLikeDto/ObjectWithNotNullArrayAccessPropertyDto.php create mode 100644 tests/Fixtures/ArrayLikeDto/ObjectWithNotNullTraversablePropertyDto.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e43ace..67304be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * feat(`PresetMappingFactory`): Add `fromObjectCache()` and `fromObjectCacheReversed()`. * chore: Simplify remembering mapper. * refactor: Deprecate serializer context. +* feat: null to `Traversable` or `ArrayAccess` is now handled & returns empty. ## 1.0.0 diff --git a/psalm.xml b/psalm.xml index d18deb9..c4f05cd 100644 --- a/psalm.xml +++ b/psalm.xml @@ -52,6 +52,8 @@ + + diff --git a/src/Transformer/Implementation/TraversableToArrayAccessTransformer.php b/src/Transformer/Implementation/TraversableToArrayAccessTransformer.php index 03eccd6..4a5ec53 100644 --- a/src/Transformer/Implementation/TraversableToArrayAccessTransformer.php +++ b/src/Transformer/Implementation/TraversableToArrayAccessTransformer.php @@ -51,6 +51,10 @@ public function transform( ?Type $targetType, Context $context ): mixed { + if ($source === null) { + $source = []; + } + if ($targetType === null) { throw new InvalidArgumentException('Target type must not be null.', context: $context); } @@ -221,6 +225,7 @@ public function getSupportedTransformation(): iterable $sourceTypes = [ TypeFactory::objectOfClass(\Traversable::class), TypeFactory::array(), + TypeFactory::null(), ]; $targetTypes = [ diff --git a/src/Transformer/Implementation/TraversableToTraversableTransformer.php b/src/Transformer/Implementation/TraversableToTraversableTransformer.php index 72e54ee..8a978a2 100644 --- a/src/Transformer/Implementation/TraversableToTraversableTransformer.php +++ b/src/Transformer/Implementation/TraversableToTraversableTransformer.php @@ -44,6 +44,10 @@ public function transform( ?Type $targetType, Context $context ): mixed { + if ($source === null) { + $source = []; + } + if ($targetType === null) { throw new InvalidArgumentException('Target type must not be null.', context: $context); } @@ -103,6 +107,7 @@ public function getSupportedTransformation(): iterable $sourceTypes = [ TypeFactory::objectOfClass(\Traversable::class), TypeFactory::array(), + TypeFactory::null(), ]; $targetTypes = [ diff --git a/tests/Fixtures/ArrayLike/ObjectWithNullCollectionProperty.php b/tests/Fixtures/ArrayLike/ObjectWithNullCollectionProperty.php new file mode 100644 index 0000000..c266255 --- /dev/null +++ b/tests/Fixtures/ArrayLike/ObjectWithNullCollectionProperty.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\ArrayLike; + +use Doctrine\Common\Collections\Collection; +use Rekalogika\Mapper\Tests\Fixtures\Scalar\ObjectWithScalarProperties; + +class ObjectWithNullCollectionProperty +{ + /** + * @var null|Collection + */ + public ?Collection $property = null; +} diff --git a/tests/Fixtures/ArrayLikeDto/ObjectWithNotNullArrayAccessPropertyDto.php b/tests/Fixtures/ArrayLikeDto/ObjectWithNotNullArrayAccessPropertyDto.php new file mode 100644 index 0000000..5a0e010 --- /dev/null +++ b/tests/Fixtures/ArrayLikeDto/ObjectWithNotNullArrayAccessPropertyDto.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto; + +use Rekalogika\Mapper\Tests\Fixtures\ScalarDto\ObjectWithScalarPropertiesDto; + +class ObjectWithNotNullArrayAccessPropertyDto +{ + /** + * @var \ArrayAccess + */ + public \ArrayAccess $property; +} diff --git a/tests/Fixtures/ArrayLikeDto/ObjectWithNotNullTraversablePropertyDto.php b/tests/Fixtures/ArrayLikeDto/ObjectWithNotNullTraversablePropertyDto.php new file mode 100644 index 0000000..c0427d1 --- /dev/null +++ b/tests/Fixtures/ArrayLikeDto/ObjectWithNotNullTraversablePropertyDto.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto; + +use Rekalogika\Mapper\Tests\Fixtures\ScalarDto\ObjectWithScalarPropertiesDto; + +class ObjectWithNotNullTraversablePropertyDto +{ + /** + * @var \Traversable + */ + public \Traversable $property; +} diff --git a/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php b/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php index 260f49e..002363e 100644 --- a/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php +++ b/tests/IntegrationTest/TraversableToArrayAccessMappingTest.php @@ -20,6 +20,7 @@ use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithArrayProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithArrayPropertyWithStringKey; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithCollectionProperty; +use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithNullCollectionProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithSplObjectStorageProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithTraversableProperties; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithArrayAccessPropertyDto; @@ -30,6 +31,7 @@ use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithArrayPropertyWithCompatibleHintDto; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithArrayPropertyWithoutTypeHintDto; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithCollectionPropertyDto; +use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithNotNullArrayAccessPropertyDto; use Rekalogika\Mapper\Tests\Fixtures\Scalar\ObjectWithScalarProperties; use Rekalogika\Mapper\Tests\Fixtures\ScalarDto\ObjectWithScalarPropertiesDto; use Rekalogika\Mapper\Transformer\Model\HashTable; @@ -177,6 +179,20 @@ public function testCollectionToArrayAccessDto(): void $this->assertEquals(1.1, $result->property[1]?->d); } + public function testNullToNotNullArrayAccessDto(): void + { + $source = new ObjectWithNullCollectionProperty(); + + $result = $this->mapper->map($source, ObjectWithNotNullArrayAccessPropertyDto::class); + + $this->assertInstanceOf(ObjectWithNotNullArrayAccessPropertyDto::class, $result); + + $property = $result->property; + $this->assertInstanceOf(LazyArray::class, $property); + // @phpstan-ignore-next-line + $this->assertInstanceOf(\ArrayAccess::class, $property); + } + public function testArrayToArrayInterfaceDto(): void { $source = new ObjectWithArrayProperty(); diff --git a/tests/IntegrationTest/TraversableToTraversableMappingTest.php b/tests/IntegrationTest/TraversableToTraversableMappingTest.php index 2e46232..320989d 100644 --- a/tests/IntegrationTest/TraversableToTraversableMappingTest.php +++ b/tests/IntegrationTest/TraversableToTraversableMappingTest.php @@ -17,7 +17,9 @@ use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithArrayProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithLazyDoctrineCollectionProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithLazyDoctrineCollectionWithPresetCountableProperty; +use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithNullCollectionProperty; use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithTraversableProperties; +use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithNotNullTraversablePropertyDto; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithTraversablePropertyDto; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithTraversablePropertyWithoutTypeHintDto; use Rekalogika\Mapper\Tests\Fixtures\Scalar\ObjectWithScalarProperties; @@ -121,4 +123,15 @@ public function testArrayToTraversableWithoutTypehint(): void $this->assertEquals(1.1, $item->d); } } + + public function testNullToNotNullTraversableDto(): void + { + $source = new ObjectWithNullCollectionProperty(); + $result = $this->mapper->map($source, ObjectWithNotNullTraversablePropertyDto::class); + + $this->assertInstanceOf(\Traversable::class, $result->property); + + $arrayResult = iterator_to_array($result->property); + $this->assertEmpty($arrayResult); + } }