diff --git a/Neos.Flow/Classes/Persistence/Doctrine/CountWalker.php b/Neos.Flow/Classes/Persistence/Doctrine/CountWalker.php index f26dfb3fe1..1f2b1813e1 100644 --- a/Neos.Flow/Classes/Persistence/Doctrine/CountWalker.php +++ b/Neos.Flow/Classes/Persistence/Doctrine/CountWalker.php @@ -8,6 +8,7 @@ * with this package in the file License-BSD.txt. * * */ +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Query\AST\AggregateExpression; use Doctrine\ORM\Query\AST\PathExpression; use Doctrine\ORM\Query\AST\SelectExpression; @@ -40,6 +41,10 @@ public function walkSelectStatement(SelectStatement $AST) } } + if ($this->isDistinctRequired()) { + $AST->selectClause->isDistinct = true; + } + $pathExpression = new PathExpression( PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, @@ -57,4 +62,14 @@ public function walkSelectStatement(SelectStatement $AST) // ORDER BY is not needed, only increases query execution through unnecessary sorting. $AST->orderByClause = null; } + + private function isDistinctRequired(): bool + { + foreach ($this->getQueryComponents() as $queryComponent) { + if (isset($queryComponent['relation']['type']) && $queryComponent['relation']['type'] === ClassMetadataInfo::ONE_TO_MANY) { + return true; + } + } + return false; + } } diff --git a/Neos.Flow/Tests/Functional/Persistence/Doctrine/QueryTest.php b/Neos.Flow/Tests/Functional/Persistence/Doctrine/QueryTest.php index 0f31948d0b..95eee64051 100644 --- a/Neos.Flow/Tests/Functional/Persistence/Doctrine/QueryTest.php +++ b/Neos.Flow/Tests/Functional/Persistence/Doctrine/QueryTest.php @@ -353,6 +353,45 @@ public function comlexQueryWithJoinsCanBeExecutedAfterDeserialization() self::assertEquals([$testEntity2], $unserializedQuery->execute()->toArray()); } + /** + * @test + */ + public function countReturnsCorrectNumberOfEntities() + { + $testEntityRepository = new \Neos\Flow\Tests\Functional\Persistence\Fixtures\TestEntityRepository(); + $testEntityRepository->removeAll(); + + $testEntity = new \Neos\Flow\Tests\Functional\Persistence\Fixtures\TestEntity; + $testEntity->setName('Flow'); + + $subEntity1 = new \Neos\Flow\Tests\Functional\Persistence\Fixtures\SubEntity; + $subEntity1->setContent('foo'); + $subEntity1->setParentEntity($testEntity); + $testEntity->addSubEntity($subEntity1); + $this->persistenceManager->add($subEntity1); + + $subEntity2 = new \Neos\Flow\Tests\Functional\Persistence\Fixtures\SubEntity; + $subEntity2->setContent('foo'); + $subEntity2->setParentEntity($testEntity); + $testEntity->addSubEntity($subEntity2); + $this->persistenceManager->add($subEntity2); + + $testEntityRepository->add($testEntity); + + $this->persistenceManager->persistAll(); + + $query = new Query(\Neos\Flow\Tests\Functional\Persistence\Fixtures\TestEntity::class); + + $constraint = $query->logicalAnd($query->equals('subEntities.content', 'foo')); + $result = $query->matching($constraint)->execute(); + + $count = $result->count(); + $arrayCount = $result->toArray(); + + self::assertEquals(1, count($arrayCount), 'This correctly returns 1'); + self::assertEquals(1, $count, 'this returns 2'); + } + protected function assertQueryEquals(Query $expected, Query $actual) { self::assertEquals($expected->getConstraint(), $actual->getConstraint());