SearchKit - Support filters in new Run action
authorColeman Watts <coleman@civicrm.org>
Fri, 12 Feb 2021 17:33:56 +0000 (12:33 -0500)
committerColeman Watts <coleman@civicrm.org>
Mon, 15 Feb 2021 02:22:36 +0000 (21:22 -0500)
This uses the global 'includeWildCardInName' setting to determine whether to do a full wildcard search.

Civi/Api4/Query/Api4SelectQuery.php
ext/search/Civi/Api4/Action/SearchDisplay/Run.php

index 073e36ac29e8f82fd575644dd10993413ff1f6cc..e604e99bea50d44c7b238c782b1a2d9ab84e72e5 100644 (file)
@@ -537,7 +537,9 @@ class Api4SelectQuery {
     if ($strict && !$field) {
       throw new \API_Exception("Invalid field '$fieldName'");
     }
-    $this->apiFieldSpec[$expr] = $field;
+    if ($field) {
+      $this->apiFieldSpec[$expr] = $field;
+    }
     return $field;
   }
 
index d50bd79e9cb0e9878de54efe4f0cf92f41af29f2..1fd8d26c08bda5828b4a613a763e01f393e3e775 100644 (file)
@@ -40,14 +40,21 @@ class Run extends \Civi\Api4\Generic\AbstractAction {
    */
   protected $return;
 
+  private $_selectQuery;
+
   /**
    * Search conditions that will be automatically added to the WHERE or HAVING clauses
    * @var array
    */
   protected $filters = [];
 
+  /**
+   * @param \Civi\Api4\Generic\Result $result
+   * @throws UnauthorizedException
+   * @throws \API_Exception
+   */
   public function _run(\Civi\Api4\Generic\Result $result) {
-    // Only administrators can use this in the unsecured "preview mode"
+    // Only administrators can use this in unsecured "preview mode"
     if (!(is_string($this->savedSearch) && is_string($this->display)) && $this->checkPermissions && !\CRM_Core_Permission::check('administer CiviCRM')) {
       throw new UnauthorizedException('Access denied');
     }
@@ -95,15 +102,50 @@ class Run extends \Civi\Api4\Generic\AbstractAction {
             $apiParams['select'][] = $idField;
           }
         }
-
     }
 
+    $this->applyFilters();
+
     $apiResult = civicrm_api4($entityName, 'get', $apiParams);
 
     $result->rowCount = $apiResult->rowCount;
     $result->exchangeArray($apiResult->getArrayCopy());
   }
 
+  /**
+   * Applies supplied filters to the where clause
+   */
+  private function applyFilters() {
+    // Global setting determines if % wildcard should be added to both sides (default) or only the end of the search term
+    $prefixWithWildcard = \Civi::settings()->get('includeWildCardInName');
+
+    foreach ($this->filters as $fieldName => $value) {
+      if ($value) {
+        $field = $this->getField($fieldName) ?? [];
+        $dataType = $field['data_type'] ?? NULL;
+
+        if (!empty($field['serialize'])) {
+          $this->savedSearch['api_params']['where'][] = [$fieldName, 'CONTAINS', $value];
+        }
+        elseif (!empty($field['options']) || in_array($dataType, ['Integer', 'Boolean', 'Date', 'Timestamp'])) {
+          $this->savedSearch['api_params']['where'][] = [$fieldName, '=', $value];
+        }
+        elseif ($prefixWithWildcard) {
+          $this->savedSearch['api_params']['where'][] = [$fieldName, 'CONTAINS', $value];
+        }
+        else {
+          $this->savedSearch['api_params']['where'][] = [$fieldName, 'LIKE', $value . '%'];
+        }
+      }
+    }
+  }
+
+  /**
+   * Transforms the SORT param (which is expected to be an array of arrays)
+   * to the ORDER BY clause (which is an associative array of [field => DIR]
+   *
+   * @return array
+   */
   private function getOrderByFromSort() {
     $defaultSort = $this->display['settings']['sort'] ?? [];
     $currentSort = $this->sort;
@@ -122,12 +164,22 @@ class Run extends \Civi\Api4\Generic\AbstractAction {
     return $orderBy;
   }
 
+  /**
+   * Returns an array of field names or aliases from the SELECT clause
+   * @return string[]
+   */
   private function getSelectAliases() {
     return array_map(function($select) {
       return array_slice(explode(' AS ', $select), -1)[0];
     }, $this->savedSearch['api_params']['select']);
   }
 
+  /**
+   * Determines if a column is eligible to use an aggregate function
+   * @param $fieldName
+   * @param $prefix
+   * @return bool
+   */
   private function canAggregate($fieldName, $prefix) {
     $apiParams = $this->savedSearch['api_params'];
 
@@ -143,4 +195,17 @@ class Run extends \Civi\Api4\Generic\AbstractAction {
     return !in_array($prefix . 'id', $apiParams['groupBy']);
   }
 
+  /**
+   * Returns field definition for a given field or NULL if not found
+   * @param $fieldName
+   * @return array|null
+   */
+  private function getField($fieldName) {
+    if (!$this->_selectQuery) {
+      $api = \Civi\API\Request::create($this->savedSearch['api_entity'], 'get', $this->savedSearch['api_params']);
+      $this->_selectQuery = new \Civi\Api4\Query\Api4SelectQuery($api);
+    }
+    return $this->_selectQuery->getField($fieldName, FALSE);
+  }
+
 }