From af3d83d8bf40ea34d6d6d6b9475a24927d1e39e4 Mon Sep 17 00:00:00 2001 From: Alix Mauro Date: Fri, 19 Jan 2024 14:16:24 +0100 Subject: [PATCH] Consider usage of setFetchMode when checking for simultaneous usage of fetch-mode EAGER and WITH condition. This fixes a bug that arises when an entity relation is mapped with fetch-mode EAGER but setFetchMode LAZY (or anything that is not EAGER) has been used on the query. If the query use WITH condition, an exception is incorrectly raised (Associations with fetch-mode=EAGER may not be using WITH conditions). Fixes #11128 --- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- .../ORM/Functional/Ticket/GH11128Test.php | 97 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/GH11128Test.php diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 34e5b22580d..afbd309cd30 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1047,7 +1047,7 @@ public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joi } } - if ($relation['fetch'] === ClassMetadata::FETCH_EAGER && $condExpr !== null) { + if (($this->query->getHint('fetchMode')[$assoc['sourceEntity']][$assoc['fieldName']] ?? $relation['fetch']) === ClassMetadata::FETCH_EAGER && $condExpr !== null) { throw QueryException::eagerFetchJoinWithNotAllowed($assoc['sourceEntity'], $assoc['fieldName']); } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH11128Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH11128Test.php new file mode 100644 index 00000000000..ec8a6eaeed0 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH11128Test.php @@ -0,0 +1,97 @@ +setUpEntitySchema([ + GH11128Owner::class, + GH11128Inverse::class, + ]); + } + + public function testSetFetchModeEagerWhenNotEagerInMapping(): void + { + $query = $this->_em->createQueryBuilder() + ->select('o', 'i') + ->from(GH11128Owner::class, 'o') + ->innerJoin('o.inverseLazy', 'i', Query\Expr\Join::WITH, 'i.field IS NOT NULL') + ->getQuery(); + $query->setFetchMode(GH11128Owner::class, 'inverseLazy', ORM\ClassMetadataInfo::FETCH_EAGER); + $this->expectExceptionMessage('Associations with fetch-mode=EAGER may not be using WITH conditions in + "' . GH11128Owner::class . '#inverseLazy".'); + $query->getSql(); + } + + public function testSetFetchModeNotEagerWhenEagerInMapping(): void + { + $query = $this->_em->createQueryBuilder() + ->select('o', 'i') + ->from(GH11128Owner::class, 'o') + ->innerJoin('o.inverseEager', 'i', Query\Expr\Join::WITH, 'i.field IS NOT NULL') + ->getQuery(); + $query->setFetchMode(GH11128Owner::class, 'inverseEager', ORM\ClassMetadataInfo::FETCH_LAZY); + $this->assertIsString($query->getSql()); + } +} + +/** + * @ORM\Entity + */ +class GH11128Owner +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * + * @var ?int + */ + public $id; + + /** + * @ORM\ManyToOne(targetEntity="GH11128Inverse", fetch="EAGER") + * + * @var GH11128Inverse + */ + private $inverseEager; + + /** + * @ORM\ManyToOne(targetEntity="GH11128Inverse", fetch="LAZY") + * + * @var GH11128Inverse + */ + private $inverseLazy; +} + +/** + * @ORM\Entity + */ +class GH11128Inverse +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * + * @var ?int + */ + public $id; + + /** + * @ORM\Column(type="text", nullable=true) + * + * @var string + */ + private $field; +}