diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index 922f0954a203..a5aa319ac806 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -535,7 +535,8 @@ private function addExplicitJoins() { $side = array_shift($join) ? 'INNER' : 'LEFT'; // Add all fields from joined entity to spec $joinEntityGet = \Civi\API\Request::create($entity, 'get', ['version' => 4, 'checkPermissions' => $this->getCheckPermissions()]); - foreach ($joinEntityGet->entityFields() as $field) { + $joinEntityFields = $joinEntityGet->entityFields(); + foreach ($joinEntityFields as $field) { $field['sql_name'] = '`' . $alias . '`.`' . $field['column_name'] . '`'; $this->addSpecField($alias . '.' . $field['name'], $field); } @@ -543,7 +544,7 @@ private function addExplicitJoins() { $conditions = $this->getBridgeJoin($join, $entity, $alias); } else { - $conditions = $this->getJoinConditions($join, $entity, $alias); + $conditions = $this->getJoinConditions($join, $entity, $alias, $joinEntityFields); } foreach (array_filter($join) as $clause) { $conditions[] = $this->treeWalkClauses($clause, 'ON'); @@ -559,20 +560,29 @@ private function addExplicitJoins() { * @param array $joinTree * @param string $joinEntity * @param string $alias + * @param array $joinEntityFields * @return array */ - private function getJoinConditions($joinTree, $joinEntity, $alias) { + private function getJoinConditions($joinTree, $joinEntity, $alias, $joinEntityFields) { $conditions = []; // getAclClause() expects a stack of 1-to-1 join fields to help it dedupe, but this is more flexible, // so unless this is a direct 1-to-1 join with the main entity, we'll just hack it // with a padded empty stack to bypass its deduping. $stack = [NULL, NULL]; - // If we're not explicitly referencing the joinEntity ID in the ON clause, search for a default - $explicitId = array_filter($joinTree, function($clause) use ($alias) { + // If we're not explicitly referencing the ID (or some other FK field) of the joinEntity in the ON clause, search for a default + $explicitFK = array_filter($joinTree, function($clause) use ($alias, $joinEntityFields) { list($sideA, $op, $sideB) = array_pad((array) $clause, 3, NULL); - return $op === '=' && ($sideA === "$alias.id" || $sideB === "$alias.id"); + if ($op !== '=' || !$sideB) { + return FALSE; + } + foreach ([$sideA, $sideB] as $expr) { + if ($expr === "$alias.id" || !empty($joinEntityFields["$alias.$expr"]['fk_entity'])) { + return TRUE; + } + } + return FALSE; }); - if (!$explicitId) { + if (!$explicitFK) { foreach ($this->apiFieldSpec as $name => $field) { if ($field['entity'] !== $joinEntity && $field['fk_entity'] === $joinEntity) { $conditions[] = $this->treeWalkClauses([$name, '=', "$alias.id"], 'ON');