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);