From afb75a23cfcd10d705c0d678ad7916112f5e8a0e Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 3 Nov 2021 15:55:59 -0400 Subject: [PATCH] SearchKit - Improve pseudoconstant support & tests for SearchDisplay::getDefault --- CRM/Utils/Array.php | 15 +++++++++++ Civi/Api4/Generic/BasicGetAction.php | 3 +-- .../Generic/Traits/CustomValueActionTrait.php | 2 +- Civi/Api4/Generic/Traits/DAOActionTrait.php | 2 +- Civi/Api4/Query/Api4SelectQuery.php | 2 +- Civi/Api4/Utils/FormattingUtil.php | 6 ++--- .../Api4/Action/SearchDisplay/GetDefault.php | 27 +++++++++++++------ .../SavedSearchInspectorTrait.php | 2 +- .../v4/SearchDisplay/SearchDisplayTest.php | 16 ++++++++++- 9 files changed, 57 insertions(+), 18 deletions(-) diff --git a/CRM/Utils/Array.php b/CRM/Utils/Array.php index f507f7978b..a4778fe286 100644 --- a/CRM/Utils/Array.php +++ b/CRM/Utils/Array.php @@ -1336,4 +1336,19 @@ class CRM_Utils_Array { return NULL; } + /** + * Prepend string prefix to every key in an array + * + * @param array $collection + * @param string $prefix + * @return array + */ + public static function prefixKeys(array $collection, string $prefix) { + $result = []; + foreach ($collection as $key => $value) { + $result[$prefix . $key] = $value; + } + return $result; + } + } diff --git a/Civi/Api4/Generic/BasicGetAction.php b/Civi/Api4/Generic/BasicGetAction.php index 1f784cc6b9..cdf071b620 100644 --- a/Civi/Api4/Generic/BasicGetAction.php +++ b/Civi/Api4/Generic/BasicGetAction.php @@ -112,14 +112,13 @@ class BasicGetAction extends AbstractGetAction { $pseudofield = $field['name'] . ':' . $suffix; if (!isset($values[$pseudofield]) && isset($values[$field['name']]) && $this->_isFieldSelected($pseudofield)) { $values[$pseudofield] = $values[$field['name']]; - $fields[$pseudofield] = $field; } } } } } // Swap raw values with pseudoconstants - FormattingUtil::formatOutputValues($records, $fields, $this->getEntityName(), $this->getActionName()); + FormattingUtil::formatOutputValues($records, $fields, $this->getActionName()); } } diff --git a/Civi/Api4/Generic/Traits/CustomValueActionTrait.php b/Civi/Api4/Generic/Traits/CustomValueActionTrait.php index d70c29f7fc..280f5bd75b 100644 --- a/Civi/Api4/Generic/Traits/CustomValueActionTrait.php +++ b/Civi/Api4/Generic/Traits/CustomValueActionTrait.php @@ -84,7 +84,7 @@ trait CustomValueActionTrait { $items[$idx]['id'] = (int) \CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM ' . $tableName); } } - FormattingUtil::formatOutputValues($items, $this->entityFields(), $this->getEntityName(), 'create'); + FormattingUtil::formatOutputValues($items, $this->entityFields(), 'create'); return $items; } diff --git a/Civi/Api4/Generic/Traits/DAOActionTrait.php b/Civi/Api4/Generic/Traits/DAOActionTrait.php index c032e762f5..98707aff3b 100644 --- a/Civi/Api4/Generic/Traits/DAOActionTrait.php +++ b/Civi/Api4/Generic/Traits/DAOActionTrait.php @@ -161,7 +161,7 @@ trait DAOActionTrait { } } - FormattingUtil::formatOutputValues($result, $this->entityFields(), $this->getEntityName()); + FormattingUtil::formatOutputValues($result, $this->entityFields()); return $result; } diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index da69fd0b9d..29f9345cbb 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -174,7 +174,7 @@ class Api4SelectQuery { } $results[] = $result; } - FormattingUtil::formatOutputValues($results, $this->apiFieldSpec, $this->getEntity(), 'get', $this->selectAliases); + FormattingUtil::formatOutputValues($results, $this->apiFieldSpec, 'get', $this->selectAliases); return $results; } diff --git a/Civi/Api4/Utils/FormattingUtil.php b/Civi/Api4/Utils/FormattingUtil.php index 8bcc5de9c3..f537e73b5e 100644 --- a/Civi/Api4/Utils/FormattingUtil.php +++ b/Civi/Api4/Utils/FormattingUtil.php @@ -185,20 +185,20 @@ class FormattingUtil { * * @param array $results * @param array $fields - * @param string $entity * @param string $action * @param array $selectAliases * @throws \API_Exception * @throws \CRM_Core_Exception */ - public static function formatOutputValues(&$results, $fields, $entity, $action = 'get', $selectAliases = []) { + public static function formatOutputValues(&$results, $fields, $action = 'get', $selectAliases = []) { $fieldOptions = []; foreach ($results as &$result) { $contactTypePaths = []; foreach ($result as $key => $value) { $fieldExpr = SqlExpression::convert($selectAliases[$key] ?? $key); $fieldName = \CRM_Utils_Array::first($fieldExpr->getFields()); - $field = $fieldName && isset($fields[$fieldName]) ? $fields[$fieldName] : NULL; + $baseName = $fieldName ? \CRM_Utils_Array::first(explode(':', $fieldName)) : NULL; + $field = $fields[$fieldName] ?? $fields[$baseName] ?? NULL; $dataType = $field['data_type'] ?? ($fieldName == 'id' ? 'Integer' : NULL); // Allow Sql Functions to do special formatting and/or alter the $dataType if (method_exists($fieldExpr, 'formatOutputValue') && is_string($value)) { diff --git a/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetDefault.php b/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetDefault.php index 2e0ec09152..72bc09b69d 100644 --- a/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetDefault.php +++ b/ext/search_kit/Civi/Api4/Action/SearchDisplay/GetDefault.php @@ -2,6 +2,8 @@ namespace Civi\Api4\Action\SearchDisplay; +use Civi\Api4\SavedSearch; +use Civi\Api4\Utils\FormattingUtil; use Civi\Search\Display; use CRM_Search_ExtensionUtil as E; use Civi\Api4\Query\SqlEquation; @@ -58,9 +60,6 @@ class GetDefault extends \Civi\Api4\Generic\AbstractAction { 'saved_search_id' => $this->savedSearch['id'] ?? NULL, 'label' => $label, 'type' => 'table', - 'type:label' => E::ts('Table'), - 'type:name' => 'crm-search-display-table', - 'type:icon' => 'fa-table', 'acl_bypass' => FALSE, 'settings' => [ 'actions' => TRUE, @@ -73,10 +72,6 @@ class GetDefault extends \Civi\Api4\Generic\AbstractAction { 'columns' => [], ], ]; - // Allow implicit-join-style selection of saved search fields - foreach ($this->savedSearch as $key => $val) { - $display['saved_search_id.' . $key] = $val; - } foreach ($this->getSelectClause() as $key => $clause) { $display['settings']['columns'][] = $this->configureColumn($clause, $key); } @@ -89,7 +84,23 @@ class GetDefault extends \Civi\Api4\Generic\AbstractAction { 'alignment' => 'text-right', 'links' => $this->getLinksMenu(), ]; - $result->exchangeArray($this->selectArray([$display])); + $fields = $this->entityFields(); + // Allow implicit-join-style selection of saved search fields + if ($this->savedSearch) { + $display += \CRM_Utils_Array::prefixKeys($this->savedSearch, 'saved_search_id.'); + $fields += \CRM_Utils_Array::prefixKeys(SavedSearch::get()->entityFields(), 'saved_search_id.'); + } + // Fill pseudoconstant keys with raw values for replacement + foreach ($this->select as $fieldExpr) { + [$fieldName, $suffix] = array_pad(explode(':', $fieldExpr), 2, NULL); + if ($suffix && array_key_exists($fieldName, $display)) { + $display[$fieldExpr] = $display[$fieldName]; + } + } + $results = [$display]; + // Replace pseudoconstants + FormattingUtil::formatOutputValues($results, $fields); + $result->exchangeArray($this->selectArray($results)); } /** diff --git a/ext/search_kit/Civi/Api4/Action/SearchDisplay/SavedSearchInspectorTrait.php b/ext/search_kit/Civi/Api4/Action/SearchDisplay/SavedSearchInspectorTrait.php index 7b981d016a..84a45f78aa 100644 --- a/ext/search_kit/Civi/Api4/Action/SearchDisplay/SavedSearchInspectorTrait.php +++ b/ext/search_kit/Civi/Api4/Action/SearchDisplay/SavedSearchInspectorTrait.php @@ -48,7 +48,7 @@ trait SavedSearchInspectorTrait { ->addWhere('name', '=', $this->savedSearch) ->execute()->single(); } - $this->_apiParams = $this->savedSearch['api_params'] + ['where' => [], 'join' => [], 'having' => []]; + $this->_apiParams = ($this->savedSearch['api_params'] ?? []) + ['select' => [], 'where' => [], 'join' => [], 'having' => []]; } /** diff --git a/ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchDisplayTest.php b/ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchDisplayTest.php index c75d98fd98..eebceeaee1 100644 --- a/ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchDisplayTest.php +++ b/ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchDisplayTest.php @@ -27,13 +27,27 @@ class SearchDisplayTest extends \PHPUnit\Framework\TestCase implements HeadlessI ]; $display = SearchDisplay::getDefault(FALSE) ->setSavedSearch($params) - ->addSelect('*', 'saved_search_id.api_entity', 'type:name') + ->addSelect('*', 'saved_search_id.api_entity', 'saved_search_id.api_entity:label', 'type:name', 'type:icon') ->execute()->single(); $this->assertCount(5, $display['settings']['columns']); $this->assertEquals('Contacts', $display['label']); $this->assertEquals('crm-search-display-table', $display['type:name']); + $this->assertEquals('fa-table', $display['type:icon']); $this->assertEquals('Contact', $display['saved_search_id.api_entity']); + $this->assertEquals('Contacts', $display['saved_search_id.api_entity:label']); + } + + public function testGetDefaultNoEntity() { + $display = SearchDisplay::getDefault(FALSE) + ->addSelect('*', 'saved_search_id', 'type:name', 'type:icon') + ->execute()->single(); + + $this->assertCount(1, $display['settings']['columns']); + $this->assertEquals('', $display['label']); + $this->assertEquals('crm-search-display-table', $display['type:name']); + $this->assertEquals('fa-table', $display['type:icon']); + $this->assertNull($display['saved_search_id']); } } -- 2.25.1