Skip to content

Commit

Permalink
dev/core#2271 - APIv4 - Smarter check for adding default ON clause to…
Browse files Browse the repository at this point in the history
… explicit joins

When adding explicit joins in APIv4, the API will automacially add an ON clause linking it
to the base entity unless it detects some other link already present in the ON clause supplied.
Previously it would determine this by checking for the join entity ID;
now it looks for the join entity ID OR any other field in the join entity with an FK.
This gives more leeway for people to add creative joins without the defaults getting in the way.
  • Loading branch information
colemanw committed Dec 24, 2020
1 parent b2b9e8c commit 3fd2e5a
Showing 1 changed file with 17 additions and 7 deletions.
24 changes: 17 additions & 7 deletions Civi/Api4/Query/Api4SelectQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -535,15 +535,16 @@ 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);
}
if (!empty($join[0]) && is_string($join[0]) && \CRM_Utils_Rule::alphanumeric($join[0])) {
$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');
Expand All @@ -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');
Expand Down

0 comments on commit 3fd2e5a

Please sign in to comment.