From 138158bdaa72878a8d73a5884198b74445094ece Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 20 Jul 2021 12:24:20 -0400 Subject: [PATCH] APIv4 - Fix calculated fields in bridge entities Fixes a crash-inducing bug in the use of calculated fields on a bridge entity, such as RelationshipCache.is_current. --- Civi/Api4/Query/Api4SelectQuery.php | 6 ++-- tests/phpunit/api/v4/Action/FkJoinTest.php | 37 +++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index 64d94ed4c2..7232d02b3f 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -903,14 +903,16 @@ class Api4SelectQuery { $bridgeFkFields = [$joinRef->getReferenceKey(), $joinRef->getTypeColumn(), $baseRef->getReferenceKey(), $baseRef->getTypeColumn()]; $bridgeEntityClass = CoreUtil::getApiClass($bridgeEntity); foreach ($bridgeEntityClass::get($this->getCheckPermissions())->entityFields() as $name => $field) { - if ($field['type'] !== 'Field' || $name === 'id' || ($side === 'INNER' && in_array($name, $bridgeFkFields, TRUE))) { + if ($name === 'id' || ($side === 'INNER' && in_array($name, $bridgeFkFields, TRUE))) { continue; } // For INNER joins, these fields get a sql alias pointing to the bridge entity, // but an api alias pretending they belong to the join entity. $field['sql_name'] = '`' . ($side === 'LEFT' ? $alias : $bridgeAlias) . '`.`' . $field['column_name'] . '`'; $this->addSpecField($alias . '.' . $name, $field); - $fakeFields[$field['column_name']] = '`' . $bridgeAlias . '`.`' . $field['column_name'] . '`'; + if ($field['type'] === 'Field') { + $fakeFields[$field['column_name']] = '`' . $bridgeAlias . '`.`' . $field['column_name'] . '`'; + } } return $fakeFields; } diff --git a/tests/phpunit/api/v4/Action/FkJoinTest.php b/tests/phpunit/api/v4/Action/FkJoinTest.php index 64660c78b0..d0ed5c87b1 100644 --- a/tests/phpunit/api/v4/Action/FkJoinTest.php +++ b/tests/phpunit/api/v4/Action/FkJoinTest.php @@ -264,7 +264,7 @@ class FkJoinTest extends UnitTestCase { ->setValues(['contact_id_a' => '$id', 'contact_id_b' => $cid1, 'relationship_type_id' => 1]) ) ->addChain('r2', Relationship::create() - ->setValues(['contact_id_a' => '$id', 'contact_id_b' => $cid2, 'relationship_type_id' => 2]) + ->setValues(['contact_id_a' => '$id', 'contact_id_b' => $cid2, 'relationship_type_id' => 2, 'is_active' => FALSE]) ) ->execute() ->first()['id']; @@ -308,6 +308,41 @@ class FkJoinTest extends UnitTestCase { $this->assertNull($result[3]['rel.id']); $this->assertEquals($cid3, $result[4]['contact.id']); $this->assertNull($result[3]['rel.id']); + + // Ensure calculated fields such as is_current work correctly for both LEFT and INNER joins + foreach (['LEFT', 'INNER'] as $side) { + $result = civicrm_api4('Contact', 'get', [ + 'select' => [ + 'id', + 'display_name', + 'Contact_RelationshipCache_Contact_01.id', + 'Contact_RelationshipCache_Contact_01.near_relation:label', + 'Contact_RelationshipCache_Contact_01.is_current', + 'Contact_RelationshipCache_Contact_01.relationship_id', + 'Contact_RelationshipCache_Contact_01.orientation', + ], + 'where' => [ + ['Contact_RelationshipCache_Contact_01.id', '=', $cid3], + ], + 'join' => [ + [ + 'Contact AS Contact_RelationshipCache_Contact_01', + $side, + 'RelationshipCache', + ['id', '=', 'Contact_RelationshipCache_Contact_01.far_contact_id'], + ], + ], + 'orderBy' => ['id' => 'ASC'], + 'checkPermissions' => TRUE, + 'limit' => 50, + 'offset' => 0, + ]); + $this->assertEquals($cid1, $result[0]['id']); + $this->assertEquals($cid2, $result[1]['id']); + $this->assertEquals($cid3, $result[1]['Contact_RelationshipCache_Contact_01.id']); + $this->assertEquals(TRUE, $result[0]['Contact_RelationshipCache_Contact_01.is_current']); + $this->assertEquals(FALSE, $result[1]['Contact_RelationshipCache_Contact_01.is_current']); + } } public function testJoinToEmployerId() { -- 2.25.1