Skip to content

Commit a939dc2

Browse files
authored
[GH-9219] Add support for toIterable over mixed or scalar results. (#12187)
* [GH-9219] Add support for toIterable over mixed or scalar results. * Housekeeping: phpcs * Update test names
1 parent f8186b1 commit a939dc2

File tree

3 files changed

+125
-11
lines changed

3 files changed

+125
-11
lines changed

src/AbstractQuery.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
2222
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
2323
use Doctrine\ORM\Query\Parameter;
24-
use Doctrine\ORM\Query\QueryException;
2524
use Doctrine\ORM\Query\ResultSetMapping;
2625
use Doctrine\Persistence\Mapping\MappingException;
2726
use LogicException;
@@ -1144,10 +1143,6 @@ public function toIterable(iterable $parameters = [], $hydrationMode = null): it
11441143
throw new LogicException('Uninitialized result set mapping.');
11451144
}
11461145

1147-
if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
1148-
throw QueryException::iterateWithMixedResultNotAllowed();
1149-
}
1150-
11511146
$stmt = $this->_doExecute();
11521147

11531148
return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints);

src/Internal/Hydration/AbstractHydrator.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
use function array_map;
2626
use function array_merge;
2727
use function count;
28+
use function current;
2829
use function end;
2930
use function get_debug_type;
3031
use function in_array;
3132
use function is_array;
33+
use function is_object;
3234
use function sprintf;
3335

3436
/**
@@ -201,8 +203,10 @@ public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hin
201203
} else {
202204
yield from $result;
203205
}
204-
} else {
206+
} elseif (is_object(current($result))) {
205207
yield $result;
208+
} else {
209+
yield array_merge(...$result);
206210
}
207211
}
208212
} finally {

tests/Tests/ORM/Functional/QueryTest.php

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,128 @@ public function testToIterableWithMultipleSelectElements(): void
401401
}
402402
}
403403

404-
public function testToIterableWithMixedResultIsNotAllowed(): void
404+
public function testToIterableWithMixedResultEntityScalars(): void
405405
{
406-
$this->expectException(QueryException::class);
407-
$this->expectExceptionMessage('Iterating a query with mixed results (using scalars) is not supported.');
406+
$author = new CmsUser();
407+
$author->name = 'Ben';
408+
$author->username = 'beberlei';
408409

409-
$query = $this->_em->createQuery('select a, a.topic from ' . CmsArticle::class . ' a');
410-
$query->toIterable();
410+
$article1 = new CmsArticle();
411+
$article1->topic = 'Doctrine 2';
412+
$article1->text = 'This is an introduction to Doctrine 2.';
413+
$article1->setAuthor($author);
414+
415+
$article2 = new CmsArticle();
416+
$article2->topic = 'Symfony 2';
417+
$article2->text = 'This is an introduction to Symfony 2.';
418+
$article2->setAuthor($author);
419+
420+
$article3 = new CmsArticle();
421+
$article3->topic = 'lala 2';
422+
$article3->text = 'This is an introduction to Symfony 2.';
423+
$article3->setAuthor($author);
424+
425+
$this->_em->persist($article1);
426+
$this->_em->persist($article2);
427+
$this->_em->persist($article3);
428+
$this->_em->persist($author);
429+
430+
$this->_em->flush();
431+
$this->_em->clear();
432+
433+
$query = $this->_em->createQuery('select a, a.topic, a.text from ' . CmsArticle::class . ' a');
434+
$result = $query->toIterable();
435+
436+
$it = iterator_to_array($result);
437+
$this->assertCount(3, $it);
438+
$this->assertEquals('Doctrine 2', $it[0]['topic']);
439+
$this->assertEquals('Doctrine 2', $it[0][0]->topic);
440+
$this->assertEquals('lala 2', $it[2]['topic']);
441+
$this->assertEquals('lala 2', $it[2][0]->topic);
442+
}
443+
444+
public function testToIterableWithMixedResultArbitraryJoinsScalars(): void
445+
{
446+
$author = new CmsUser();
447+
$author->name = 'Ben';
448+
$author->username = 'beberlei';
449+
450+
$article1 = new CmsArticle();
451+
$article1->topic = 'Doctrine 2';
452+
$article1->text = 'This is an introduction to Doctrine 2.';
453+
$article1->setAuthor($author);
454+
455+
$article2 = new CmsArticle();
456+
$article2->topic = 'Symfony 2';
457+
$article2->text = 'This is an introduction to Symfony 2.';
458+
$article2->setAuthor($author);
459+
460+
$article3 = new CmsArticle();
461+
$article3->topic = 'lala 2';
462+
$article3->text = 'This is an introduction to Symfony 2.';
463+
$article3->setAuthor($author);
464+
465+
$this->_em->persist($article1);
466+
$this->_em->persist($article2);
467+
$this->_em->persist($article3);
468+
$this->_em->persist($author);
469+
470+
$this->_em->flush();
471+
$this->_em->clear();
472+
473+
$query = $this->_em->createQuery('select a, u, a.topic, a.text from ' . CmsArticle::class . ' a, ' . CmsUser::class . ' u WHERE a.user = u ');
474+
$result = $query->toIterable();
475+
476+
$it = iterator_to_array($result);
477+
478+
$this->assertCount(3, $it);
479+
$this->assertEquals('Doctrine 2', $it[0]['topic']);
480+
$this->assertEquals('Doctrine 2', $it[0][0]->topic);
481+
$this->assertEquals('beberlei', $it[0][1]->username);
482+
$this->assertEquals('lala 2', $it[2]['topic']);
483+
$this->assertEquals('lala 2', $it[2][0]->topic);
484+
$this->assertEquals('beberlei', $it[2][1]->username);
485+
}
486+
487+
public function testToIterableWithMixedResultScalarsOnly(): void
488+
{
489+
$author = new CmsUser();
490+
$author->name = 'Ben';
491+
$author->username = 'beberlei';
492+
493+
$article1 = new CmsArticle();
494+
$article1->topic = 'Doctrine 2';
495+
$article1->text = 'This is an introduction to Doctrine 2.';
496+
$article1->setAuthor($author);
497+
498+
$article2 = new CmsArticle();
499+
$article2->topic = 'Symfony 2';
500+
$article2->text = 'This is an introduction to Symfony 2.';
501+
$article2->setAuthor($author);
502+
503+
$article3 = new CmsArticle();
504+
$article3->topic = 'lala 2';
505+
$article3->text = 'This is an introduction to Symfony 2.';
506+
$article3->setAuthor($author);
507+
508+
$this->_em->persist($article1);
509+
$this->_em->persist($article2);
510+
$this->_em->persist($article3);
511+
$this->_em->persist($author);
512+
513+
$this->_em->flush();
514+
$this->_em->clear();
515+
516+
$query = $this->_em->createQuery('select a.topic, a.text from ' . CmsArticle::class . ' a ');
517+
$result = $query->toIterable();
518+
519+
$it = iterator_to_array($result);
520+
521+
$this->assertEquals([
522+
['topic' => 'Doctrine 2', 'text' => 'This is an introduction to Doctrine 2.'],
523+
['topic' => 'Symfony 2', 'text' => 'This is an introduction to Symfony 2.'],
524+
['topic' => 'lala 2', 'text' => 'This is an introduction to Symfony 2.'],
525+
], $it);
411526
}
412527

413528
public function testIterateResultClearEveryCycle(): void

0 commit comments

Comments
 (0)