From 6735dc28da4c284f8f47b53a2daad9ab6ad1302e Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:46:17 +0700 Subject: [PATCH] feat: `TraversableToTraversableTransformer` now gives a `Countable` result if the source is `Countable`. --- CHANGELOG.md | 4 ++ src/Model/TraversableCountableWrapper.php | 45 +++++++++++++++++++ .../TraversableToTraversableTransformer.php | 8 +++- ...yDoctrineCollectionWithPresetCountable.php | 32 +++++++++++++ ...eCollectionWithPresetCountableProperty.php | 24 ++++++++++ .../TraversableToTraversableMappingTest.php | 22 ++++++++- 6 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/Model/TraversableCountableWrapper.php create mode 100644 tests/Fixtures/ArrayLike/LazyDoctrineCollectionWithPresetCountable.php create mode 100644 tests/Fixtures/ArrayLike/ObjectWithLazyDoctrineCollectionWithPresetCountableProperty.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 995369d..d5b84cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ mixed type. * refactor: Remove deprecated facade. * feat: Add `CopyTransformer` to handle mixed to mixed mapping. +* feat: `TraversableToTraversableTransformer` now gives a `Countable` result + if the source is `Countable`. +* revert: Revert support for `Generator` target type. Impossible to have a + `Countable` result. ## 0.5.3 diff --git a/src/Model/TraversableCountableWrapper.php b/src/Model/TraversableCountableWrapper.php new file mode 100644 index 0000000..441ab68 --- /dev/null +++ b/src/Model/TraversableCountableWrapper.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Model; + +/** + * @template TKey + * @template TValue + * @implements \IteratorAggregate + */ +final class TraversableCountableWrapper implements \IteratorAggregate, \Countable +{ + /** + * @param \Traversable $traversable + */ + public function __construct( + private \Traversable $traversable, + private \Countable|int $countable, + ) { + } + + public function getIterator(): \Traversable + { + return $this->traversable; + } + + public function count(): int + { + if (is_int($this->countable)) { + return $this->countable; + } + + return $this->countable->count(); + } +} diff --git a/src/Transformer/TraversableToTraversableTransformer.php b/src/Transformer/TraversableToTraversableTransformer.php index 163aec7..464fc89 100644 --- a/src/Transformer/TraversableToTraversableTransformer.php +++ b/src/Transformer/TraversableToTraversableTransformer.php @@ -22,6 +22,7 @@ use Rekalogika\Mapper\Exception\MissingMemberKeyTypeException; use Rekalogika\Mapper\Exception\MissingMemberValueTypeException; use Rekalogika\Mapper\MainTransformer; +use Rekalogika\Mapper\Model\TraversableCountableWrapper; use Rekalogika\Mapper\ObjectCache\ObjectCache; use Rekalogika\Mapper\ObjectCache\ObjectCacheFactoryInterface; use Rekalogika\Mapper\Util\TypeCheck; @@ -158,6 +159,12 @@ public function transform( } })(); + if ($source instanceof \Countable) { + $target = new TraversableCountableWrapper($target, $source); + } elseif (is_array($source)) { + $target = new TraversableCountableWrapper($target, count($source)); + } + $objectCache->saveTarget($source, $targetType, $target); return $target; @@ -171,7 +178,6 @@ public function getSupportedTransformation(): iterable ]; $targetTypes = [ - TypeFactory::objectOfClass(\Generator::class), TypeFactory::objectOfClass(\Traversable::class), ]; diff --git a/tests/Fixtures/ArrayLike/LazyDoctrineCollectionWithPresetCountable.php b/tests/Fixtures/ArrayLike/LazyDoctrineCollectionWithPresetCountable.php new file mode 100644 index 0000000..8cb1001 --- /dev/null +++ b/tests/Fixtures/ArrayLike/LazyDoctrineCollectionWithPresetCountable.php @@ -0,0 +1,32 @@ + + * + * 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\AbstractLazyCollection; + +/** + * @extends AbstractLazyCollection + */ +class LazyDoctrineCollectionWithPresetCountable extends AbstractLazyCollection +{ + protected function doInitialize() + { + throw new \LogicException("Not expected to be initialized"); + } + + public function count(): int + { + return 31337; + } +} diff --git a/tests/Fixtures/ArrayLike/ObjectWithLazyDoctrineCollectionWithPresetCountableProperty.php b/tests/Fixtures/ArrayLike/ObjectWithLazyDoctrineCollectionWithPresetCountableProperty.php new file mode 100644 index 0000000..b038ead --- /dev/null +++ b/tests/Fixtures/ArrayLike/ObjectWithLazyDoctrineCollectionWithPresetCountableProperty.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\ArrayLike; + +class ObjectWithLazyDoctrineCollectionWithPresetCountableProperty +{ + public LazyDoctrineCollectionWithPresetCountable $property; + + public function __construct() + { + $this->property = new LazyDoctrineCollectionWithPresetCountable(); + } +} diff --git a/tests/IntegrationTest/TraversableToTraversableMappingTest.php b/tests/IntegrationTest/TraversableToTraversableMappingTest.php index e9d4fcb..dac8476 100644 --- a/tests/IntegrationTest/TraversableToTraversableMappingTest.php +++ b/tests/IntegrationTest/TraversableToTraversableMappingTest.php @@ -13,9 +13,11 @@ namespace Rekalogika\Mapper\Tests\IntegrationTest; +use Rekalogika\Mapper\Model\TraversableCountableWrapper; use Rekalogika\Mapper\Tests\Common\AbstractIntegrationTest; 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\ObjectWithTraversableProperties; use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithTraversablePropertyDto; use Rekalogika\Mapper\Tests\Fixtures\Scalar\ObjectWithScalarPropertiesDto; @@ -54,7 +56,7 @@ public function testArrayToTraversableDto(): void $this->assertInstanceOf(ObjectWithTraversablePropertyDto::class, $result); $this->assertNotNull($result->property); - $this->assertInstanceOf(\Generator::class, $result->property); + $this->assertInstanceOf(TraversableCountableWrapper::class, $result->property); foreach ($result->property as $item) { $this->assertInstanceOf(ObjectWithScalarPropertiesDto::class, $item); @@ -74,7 +76,23 @@ public function testLazy(): void $this->assertInstanceOf(ObjectWithTraversablePropertyDto::class, $result); $this->assertNotNull($result->property); - $this->assertInstanceOf(\Generator::class, $result->property); + $this->assertInstanceOf(TraversableCountableWrapper::class, $result->property); + + $this->expectException(\LogicException::class); + foreach ($result->property as $item); + } + + public function testExtraLazy(): void + { + $source = new ObjectWithLazyDoctrineCollectionWithPresetCountableProperty(); + + $result = $this->mapper->map($source, ObjectWithTraversablePropertyDto::class); + + $this->assertInstanceOf(ObjectWithTraversablePropertyDto::class, $result); + $this->assertNotNull($result->property); + $this->assertInstanceOf(TraversableCountableWrapper::class, $result->property); + + $this->assertEquals(31337, $result->property->count()); $this->expectException(\LogicException::class); foreach ($result->property as $item);