From 77b92cfae5ee9384efc3066cc054afea3ed96a58 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Mon, 3 Apr 2023 16:16:27 -0400 Subject: [PATCH] SearchKit - Support Group.parents field and improve support for pseudo-fk fields This adds a 2nd field to the schema with options.prefetch = false (the first being Campaigns) This time the field is serialized and not a real FK, but prefetch=false makes sense because of the potentially very large number of groups in the database. --- CRM/Contact/DAO/Group.php | 15 +++++++++-- CRM/Core/PseudoConstant.php | 5 ---- Civi/Api4/Service/Spec/SpecFormatter.php | 4 +++ ext/search_kit/Civi/Search/Admin.php | 2 +- .../crmSearchAdmin.component.js | 2 +- .../crmSearchInput/entityRef.html | 2 +- tests/phpunit/api/v3/GroupTest.php | 9 ++++--- tests/phpunit/api/v4/Entity/GroupTest.php | 25 +++++++++++++++++++ xml/schema/Contact/Group.xml | 13 +++++++++- 9 files changed, 63 insertions(+), 14 deletions(-) diff --git a/CRM/Contact/DAO/Group.php b/CRM/Contact/DAO/Group.php index 2481edfcfd98..a56444d5aa62 100644 --- a/CRM/Contact/DAO/Group.php +++ b/CRM/Contact/DAO/Group.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/Group.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d9453f2a948783a20abc574cc0ba860a) + * (GenCodeChecksum:65f55d4c1449a2daf78f584395974789) */ /** @@ -555,6 +555,9 @@ public static function &fields() { 'bao' => 'CRM_Contact_BAO_Group', 'localizable' => 0, 'serialize' => self::SERIALIZE_SEPARATOR_BOOKEND, + 'html' => [ + 'type' => 'Select', + ], 'pseudoconstant' => [ 'optionGroupName' => 'group_type', 'optionEditPath' => 'civicrm/admin/options/group_type', @@ -618,8 +621,16 @@ public static function &fields() { 'bao' => 'CRM_Contact_BAO_Group', 'localizable' => 0, 'serialize' => self::SERIALIZE_COMMA, + 'html' => [ + 'type' => 'EntityRef', + 'label' => ts("Parent Group(s)"), + ], 'pseudoconstant' => [ - 'callback' => 'CRM_Core_PseudoConstant::allGroup', + 'table' => 'civicrm_group', + 'keyColumn' => 'id', + 'labelColumn' => 'title', + 'nameColumn' => 'name', + 'prefetch' => 'FALSE', ], 'add' => '2.1', ], diff --git a/CRM/Core/PseudoConstant.php b/CRM/Core/PseudoConstant.php index 3159f6772578..80a475027def 100644 --- a/CRM/Core/PseudoConstant.php +++ b/CRM/Core/PseudoConstant.php @@ -799,11 +799,6 @@ public static function &countryIsoCode($id = FALSE) { * array reference of all groups. */ public static function allGroup($groupType = NULL, $excludeHidden = TRUE) { - if ($groupType === 'validate') { - // validate gets passed through from getoptions. Handle in the deprecated - // fn rather than change the new pattern. - $groupType = NULL; - } $condition = CRM_Contact_BAO_Group::groupTypeCondition($groupType, $excludeHidden); $groupKey = ($groupType ? $groupType : 'null') . !empty($excludeHidden); diff --git a/Civi/Api4/Service/Spec/SpecFormatter.php b/Civi/Api4/Service/Spec/SpecFormatter.php index fc99a65e7928..fa1728bb5ee1 100644 --- a/Civi/Api4/Service/Spec/SpecFormatter.php +++ b/Civi/Api4/Service/Spec/SpecFormatter.php @@ -98,6 +98,10 @@ public static function arrayToField(array $data, $entity) { if ($fkAPIName || $fkClassName) { $field->setFkEntity($fkAPIName ?: CoreUtil::getApiNameFromBAO($fkClassName)); } + // For pseudo-fk fields like `civicrm_group.parents` + elseif (($data['html']['type'] ?? NULL) === 'EntityRef' && !empty($data['pseudoconstant']['table'])) { + $field->setFkEntity(CoreUtil::getApiNameFromTableName($data['pseudoconstant']['table'])); + } return $field; } diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php index 1225b9e1a999..b8866bdfc373 100644 --- a/ext/search_kit/Civi/Search/Admin.php +++ b/ext/search_kit/Civi/Search/Admin.php @@ -189,7 +189,7 @@ private static function addImplicitFKFields(array $schema):array { foreach ($schema as &$entity) { if ($entity['searchable'] !== 'bridge') { foreach (array_reverse($entity['fields'] ?? [], TRUE) as $index => $field) { - if (!empty($field['fk_entity']) && !$field['options'] && !empty($schema[$field['fk_entity']]['label_field'])) { + if (!empty($field['fk_entity']) && !$field['options'] && !$field['suffixes'] && !empty($schema[$field['fk_entity']]['label_field'])) { $isCustom = strpos($field['name'], '.'); // Custom fields: append "Contact ID" etc. to original field label if ($isCustom) { diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js index 4d1d8e9f1bc4..d5dde6b1e3ab 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js @@ -535,7 +535,7 @@ _.each(fields, function(field) { var item = { // Use options suffix if available. - id: prefix + field.name + (field.options && _.includes(field.suffixes || [], suffix.replace(':', '')) ? suffix : ''), + id: prefix + field.name + (_.includes(field.suffixes || [], suffix.replace(':', '')) ? suffix : ''), text: field.label, description: field.description }; diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchInput/entityRef.html b/ext/search_kit/ang/crmSearchTasks/crmSearchInput/entityRef.html index 0574685b8eca..1e284f37ed2e 100644 --- a/ext/search_kit/ang/crmSearchTasks/crmSearchInput/entityRef.html +++ b/ext/search_kit/ang/crmSearchTasks/crmSearchInput/entityRef.html @@ -2,7 +2,7 @@ class="form-control" ng-model="$ctrl.value" crm-autocomplete="$ctrl.getFkEntity()" - crm-autocomplete-params="{fieldName: $ctrl.field.entity + '.' + $ctrl.field.name}" + crm-autocomplete-params="{fieldName: $ctrl.field.entity + '.' + $ctrl.field.name, key: $ctrl.optionKey || null}" auto-open="true" multi="$ctrl.isMulti()" static-options="$ctrl.getAutocompleteStaticOptions()" diff --git a/tests/phpunit/api/v3/GroupTest.php b/tests/phpunit/api/v3/GroupTest.php index b335a5b695cb..70b542dfd9ec 100644 --- a/tests/phpunit/api/v3/GroupTest.php +++ b/tests/phpunit/api/v3/GroupTest.php @@ -242,13 +242,16 @@ public function testGroupCreateWithTypeAndParent() { /** * Test that an array of valid values works for group_type field. - * FIXME: Api4 + * @var int $version + * @dataProvider versionThreeAndFour */ - public function testGroupTypeWithPseudoconstantArray() { + public function testGroupTypeWithPseudoconstantArray($version) { + $this->_apiversion = $version; + $groupType = $version == 3 ? 'group_type' : 'group_type:name'; $params = [ 'name' => 'Test Group 2', 'title' => 'Test Group 2', - 'group_type' => ['Mailing List', 'Access Control'], + $groupType => ['Mailing List', 'Access Control'], 'sequential' => 1, ]; $group = $this->callAPISuccess('Group', 'create', $params); diff --git a/tests/phpunit/api/v4/Entity/GroupTest.php b/tests/phpunit/api/v4/Entity/GroupTest.php index d34d5bbbaf7b..bfe4ab1c49b8 100644 --- a/tests/phpunit/api/v4/Entity/GroupTest.php +++ b/tests/phpunit/api/v4/Entity/GroupTest.php @@ -84,4 +84,29 @@ public function testCreate() { ->execute(); } + public function testGetParents() { + $parent1 = Group::create(FALSE) + ->addValue('title', uniqid()) + ->execute()->single(); + $parent2 = Group::create(FALSE) + ->addValue('title', uniqid()) + ->execute()->single(); + $child = Group::create(FALSE) + ->addValue('title', uniqid()) + ->addValue('parents', [$parent1['id'], $parent2['id']]) + ->execute()->single(); + + $get = Group::get(FALSE) + ->addWhere('id', '=', $child['id']) + ->addSelect('parents') + ->execute()->single(); + $this->assertEquals([$parent1['id'], $parent2['id']], $get['parents']); + + $get = Group::get(FALSE) + ->addWhere('id', '=', $child['id']) + ->addSelect('parents:label') + ->execute()->single(); + $this->assertEquals([$parent1['title'], $parent2['title']], $get['parents:label']); + } + } diff --git a/xml/schema/Contact/Group.xml b/xml/schema/Contact/Group.xml index 5af8a76aa382..e6c336262094 100644 --- a/xml/schema/Contact/Group.xml +++ b/xml/schema/Contact/Group.xml @@ -150,6 +150,9 @@ group_type + + Select + SEPARATOR_BOOKEND 1.9 @@ -184,8 +187,16 @@ 2.1 COMMA - CRM_Core_PseudoConstant::allGroup + civicrm_group
+ id + name + title + FALSE
+ + EntityRef + + children