From fb4578406f495393955ac3d373f07f02d86159be Mon Sep 17 00:00:00 2001 From: Tomasz Ryba Date: Tue, 23 Apr 2024 23:31:19 +0200 Subject: [PATCH] Respect orderBy for EAGER fetch mode EAGER fetch mode ignores orderBy as of changes introduced with #8391 Fixes #11163 Fixes #11381 --- src/UnitOfWork.php | 10 +- .../ORM/Functional/Ticket/GH11163Test.php | 135 ++++++++++++++++++ 2 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 tests/Tests/ORM/Functional/Ticket/GH11163Test.php diff --git a/src/UnitOfWork.php b/src/UnitOfWork.php index 25b88221583..609bea433f8 100644 --- a/src/UnitOfWork.php +++ b/src/UnitOfWork.php @@ -3224,7 +3224,13 @@ public function triggerEagerLoads() * * @param PersistentCollection[] $collections * @param array $mapping - * @psalm-param array{targetEntity: class-string, sourceEntity: class-string, mappedBy: string, indexBy: string|null} $mapping + * @psalm-param array{ + * targetEntity: class-string, + * sourceEntity: class-string, + * mappedBy: string, + * indexBy: string|null, + * orderBy: array|null + * } $mapping */ private function eagerLoadCollections(array $collections, array $mapping): void { @@ -3241,7 +3247,7 @@ private function eagerLoadCollections(array $collections, array $mapping): void $entities[] = $collection->getOwner(); } - $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities]); + $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping['orderBy'] ?? null); $targetClass = $this->em->getClassMetadata($targetEntity); $targetProperty = $targetClass->getReflectionProperty($mappedBy); diff --git a/tests/Tests/ORM/Functional/Ticket/GH11163Test.php b/tests/Tests/ORM/Functional/Ticket/GH11163Test.php new file mode 100644 index 00000000000..927457840c5 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11163Test.php @@ -0,0 +1,135 @@ +setUpEntitySchema([ + GH11163Bucket::class, + GH11163BucketItem::class, + ]); + } + + public function tearDown(): void + { + parent::tearDown(); + + $conn = static::$sharedConn; + $conn->executeStatement('DELETE FROM GH11163BucketItem'); + $conn->executeStatement('DELETE FROM GH11163Bucket'); + } + + public function testFetchEagerModeWithOrderBy(): void + { + // Load entities into database + $this->_em->persist($bucket = new GH11163Bucket(11163)); + $this->_em->persist(new GH11163BucketItem(1, $bucket, 2)); + $this->_em->persist(new GH11163BucketItem(2, $bucket, 3)); + $this->_em->persist(new GH11163BucketItem(3, $bucket, 1)); + $this->_em->flush(); + $this->_em->clear(); + + // Fetch entity from database + $dql = 'SELECT bucket FROM ' . GH11163Bucket::class . ' bucket WHERE bucket.id = :id'; + $bucket = $this->_em->createQuery($dql) + ->setParameter('id', 11163) + ->getSingleResult(); + + // Assert associated entity is loaded eagerly + static::assertInstanceOf(GH11163Bucket::class, $bucket); + static::assertInstanceOf(PersistentCollection::class, $bucket->items); + static::assertTrue($bucket->items->isInitialized()); + + static::assertCount(3, $bucket->items); + + // Assert order of entities + static::assertSame(1, $bucket->items[0]->position); + static::assertSame(3, $bucket->items[0]->id); + + static::assertSame(2, $bucket->items[1]->position); + static::assertSame(1, $bucket->items[1]->id); + + static::assertSame(3, $bucket->items[2]->position); + static::assertSame(2, $bucket->items[2]->id); + } +} + +/** + * @ORM\Entity + */ +class GH11163Bucket +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * + * @var int + */ + private $id; + + /** + * @ORM\OneToMany( + * targetEntity=GH11163BucketItem::class, + * mappedBy="bucket", + * fetch="EAGER" + * ) + * @ORM\OrderBy({"position" = "ASC"}) + * + * @var Collection + */ + public $items; + + public function __construct(int $id) + { + $this->id = $id; + $this->items = new ArrayCollection(); + } +} + +/** + * @ORM\Entity + */ +class GH11163BucketItem +{ + /** + * @ORM\ManyToOne(targetEntity=GH11163Bucket::class, inversedBy="items") + * @ORM\JoinColumn(nullable=false) + * + * @var GH11163Bucket + */ + private $bucket; + + /** + * @ORM\Id + * @ORM\Column(type="integer") + * + * @var int + */ + public $id; + + /** + * @ORM\Column(type="integer", nullable=false) + * + * @var int + */ + public $position; + + public function __construct(int $id, GH11163Bucket $bucket, int $position) + { + $this->id = $id; + $this->bucket = $bucket; + $this->position = $position; + } +}