From 158a8158035b56e3602d1db55c19d9b1b2c169c9 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Tue, 6 Feb 2024 21:51:15 -0800 Subject: [PATCH] Move relation join/orderBy stuff to EVENT_AFTER_PREPARE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a bug where the orderBy value ends up getting retained if a query is executed directly, and then re-executed with eager-loading. orderBy gets applied to the eager-loading params via getCriteria() and causes a SQL error because the relation table isn’t joined in. --- src/fields/BaseRelationField.php | 64 +++++++++++++++----------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src/fields/BaseRelationField.php b/src/fields/BaseRelationField.php index 64e4cc012a1..f3c57dbcc26 100644 --- a/src/fields/BaseRelationField.php +++ b/src/fields/BaseRelationField.php @@ -646,42 +646,9 @@ public function normalizeValue(mixed $value, ?ElementInterface $element): mixed $relationsAlias = sprintf('relations_%s', StringHelper::randomString(10)); - if ($this->sortable && !$this->maintainHierarchy) { - $query->attachBehavior(self::class, new EventBehavior([ - ElementQuery::EVENT_BEFORE_PREPARE => function( - CancelableEvent $event, - ElementQuery $query, - ) use ($relationsAlias) { - if ($query->orderBy === '') { - $query->orderBy(["$relationsAlias.sortOrder" => SORT_ASC]); - } - }, - ])); - } - // join the relations table via EVENT_BEFORE_PREPARE so it gets joined for cloned queries as well $query->attachBehavior(sprintf('%s-once', self::class), new EventBehavior([ - ElementQuery::EVENT_BEFORE_PREPARE => function( - CancelableEvent $event, - ElementQuery $query, - ) use ($element, $relationsAlias) { - $query->innerJoin( - [$relationsAlias => DbTable::RELATIONS], - [ - 'and', - "[[$relationsAlias.targetId]] = [[elements.id]]", - [ - "$relationsAlias.sourceId" => $element->id, - "$relationsAlias.fieldId" => $this->id, - ], - [ - 'or', - ["$relationsAlias.sourceSiteId" => null], - ["$relationsAlias.sourceSiteId" => $element->siteId], - ], - ] - ); - + ElementQuery::EVENT_BEFORE_PREPARE => function(CancelableEvent $event, ElementQuery $query) { if ($this->maintainHierarchy && $query->id === null) { $structuresService = Craft::$app->getStructures(); @@ -700,7 +667,34 @@ public function normalizeValue(mixed $value, ?ElementInterface $element): mixed $query->id(array_map(fn(ElementInterface $element) => $element->id, $structureElements)); } }, - ], true)); + ElementQuery::EVENT_AFTER_PREPARE => function( + CancelableEvent $event, + ElementQuery $query, + ) use ($element, $relationsAlias) { + foreach ([$query->query, $query->subQuery] as $q) { + $q->innerJoin( + [$relationsAlias => DbTable::RELATIONS], + [ + 'and', + "[[$relationsAlias.targetId]] = [[elements.id]]", + [ + "$relationsAlias.sourceId" => $element->id, + "$relationsAlias.fieldId" => $this->id, + ], + [ + 'or', + ["$relationsAlias.sourceSiteId" => null], + ["$relationsAlias.sourceSiteId" => $element->siteId], + ], + ] + ); + + if ($this->sortable && !$this->maintainHierarchy && !$q->orderBy) { + $q->orderBy(["$relationsAlias.sortOrder" => SORT_ASC]); + } + } + }, + ])); // Prepare the query for lazy eager loading $query->prepForEagerLoading($this->handle, $element);