diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc29f80..716aed6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,16 +12,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ 8.1, 8.2, 8.3 ] + php: [ 8.1, 8.2, 8.3, 8.4 ] deps: [ highest ] - symfony: [ 6.4.*, 7.0.* ] + symfony: [ 6.4.*, 7.1.*, 7.2.* ] include: - php: 8.1 deps: lowest symfony: '*' exclude: - php: 8.1 - symfony: 7.0.* + symfony: 7.1.* + - php: 8.1 + symfony: 7.2.* steps: - uses: zenstruck/.github/actions/php-test-symfony@main with: diff --git a/composer.json b/composer.json index 315bdfd..2620ecb 100644 --- a/composer.json +++ b/composer.json @@ -46,5 +46,7 @@ }, "autoload-dev": { "psr-4": { "Zenstruck\\Collection\\Tests\\": ["tests/"] } - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/src/Collection/Doctrine/ORM/EntityResult.php b/src/Collection/Doctrine/ORM/EntityResult.php index 4eb6c6a..1c7f24f 100644 --- a/src/Collection/Doctrine/ORM/EntityResult.php +++ b/src/Collection/Doctrine/ORM/EntityResult.php @@ -27,6 +27,8 @@ use Zenstruck\Collection\IterableCollection; use Zenstruck\Collection\LazyCollection; +use function Zenstruck\collect; + /** * @author Kevin Bond * @@ -254,7 +256,13 @@ public function getIterator(): \Traversable } try { - yield from $this->query()->toIterable(hydrationMode: $this->hydrationMode ?? Query::HYDRATE_OBJECT); + $iterator = $this->query()->toIterable(hydrationMode: $this->hydrationMode ?? Query::HYDRATE_OBJECT); + + if ($this->resultModifier) { + $iterator = collect(fn() => yield from $iterator)->map($this->resultModifier); + } + + yield from $iterator; } catch (QueryException $e) { if ($e->getMessage() === QueryException::iterateWithMixedResultNotAllowed()->getMessage()) { throw new \LogicException(\sprintf('Results contain aggregate fields, call %s::withAggregates().', self::class), 0, $e); @@ -320,7 +328,7 @@ private function paginator(?Query $query = null): Paginator { $paginator = new Paginator($query ?? $this->query(), $this->fetchJoins); - if ($this->hydrationMode) { + if ($this->resultModifier || $this->hydrationMode) { $paginator->setUseOutputWalkers(false); } diff --git a/tests/Doctrine/Fixture/EntityDto.php b/tests/Doctrine/Fixture/EntityDto.php new file mode 100644 index 0000000..e53cf21 --- /dev/null +++ b/tests/Doctrine/Fixture/EntityDto.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Collection\Tests\Doctrine\Fixture; + +final class EntityDto +{ + public function __construct(public string $value) + { + } +} diff --git a/tests/Doctrine/ORM/Result/EntityDtoCallbackTest.php b/tests/Doctrine/ORM/Result/EntityDtoCallbackTest.php new file mode 100644 index 0000000..22cee0d --- /dev/null +++ b/tests/Doctrine/ORM/Result/EntityDtoCallbackTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Collection\Tests\Doctrine\ORM\Result; + +use Zenstruck\Collection\Doctrine\ORM\EntityResult; +use Zenstruck\Collection\Tests\Doctrine\Fixture\Entity; +use Zenstruck\Collection\Tests\Doctrine\Fixture\EntityDto; +use Zenstruck\Collection\Tests\Doctrine\ORM\EntityResultTest; + +/** + * @author Kevin Bond + */ +final class EntityDtoCallbackTest extends EntityResultTest +{ + protected function expectedValueAt(int $position) + { + return new EntityDto((string) $position); + } + + protected function createWithItems(int $count): EntityResult + { + $this->persistEntities($count); + + return (new EntityResult($this->em->createQueryBuilder()->select('e.id')->from(Entity::class, 'e'))) + ->as(fn(array $v) => new EntityDto((string) $v['id'])) + ; + } +} diff --git a/tests/Doctrine/ORM/Result/FloatResultTest.php b/tests/Doctrine/ORM/Result/FloatResultTest.php index 81adc04..3fa488b 100644 --- a/tests/Doctrine/ORM/Result/FloatResultTest.php +++ b/tests/Doctrine/ORM/Result/FloatResultTest.php @@ -32,6 +32,16 @@ public function null_result(): void $this->assertSame([], \array_filter($result->eager()->all())); } + /** + * @test + */ + public function iterator_exact_match(): void + { + $results = $this->createWithItems(3); + + $this->assertSame([1.0, 2.0, 3.0], \iterator_to_array($results)); + } + protected function expectedValueAt(int $position) { return (float) $position; diff --git a/tests/Doctrine/ORM/Result/IntResultTest.php b/tests/Doctrine/ORM/Result/IntResultTest.php index db1519d..dcdc823 100644 --- a/tests/Doctrine/ORM/Result/IntResultTest.php +++ b/tests/Doctrine/ORM/Result/IntResultTest.php @@ -31,6 +31,16 @@ public function can_select_single_scalar(): void $this->assertSame(6, $result->first()); } + /** + * @test + */ + public function iterator_exact_match(): void + { + $results = $this->createWithItems(3); + + $this->assertSame([1, 2, 3], \iterator_to_array($results)); + } + protected function expectedValueAt(int $position) { return $position; diff --git a/tests/Doctrine/ORM/Result/StringResultTest.php b/tests/Doctrine/ORM/Result/StringResultTest.php index 28e7be8..ea32122 100644 --- a/tests/Doctrine/ORM/Result/StringResultTest.php +++ b/tests/Doctrine/ORM/Result/StringResultTest.php @@ -20,6 +20,16 @@ */ final class StringResultTest extends EntityResultTest { + /** + * @test + */ + public function iterator_exact_match(): void + { + $results = $this->createWithItems(3); + + $this->assertSame(['1', '2', '3'], \iterator_to_array($results)); + } + protected function expectedValueAt(int $position) { return (string) $position;