Merge pull request #17384 from eileenmcnaughton/abort
[civicrm-core.git] / api / v3 / Generic / Getlist.php
index 464bd09be028aad1af05c47a18075f3612241a82..f63948e27c2ce66b5e673aeb281d549dabbb2185 100644 (file)
  * @param array $apiRequest
  *
  * @return mixed
+ * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_generic_getList($apiRequest) {
-  $entity = _civicrm_api_get_entity_name_from_camel($apiRequest['entity']);
+  $entity = CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($apiRequest['entity']);
   $request = $apiRequest['params'];
   $meta = civicrm_api3_generic_getfields(['action' => 'get'] + $apiRequest, FALSE);
 
@@ -37,6 +38,31 @@ function civicrm_api3_generic_getList($apiRequest) {
 
   $request['params']['check_permissions'] = !empty($apiRequest['params']['check_permissions']);
   $result = civicrm_api3($entity, 'get', $request['params']);
+  if (!empty($request['input']) && !empty($defaults['search_field_fallback']) && $result['count'] < $request['params']['options']['limit']) {
+    // We support a field fallback. Note we don't do this as an OR query because that could easily
+    // bypass an index & kill the server. We just 'pad' the results if needed with the second
+    // query - this is effectively the same as what the old Ajax::getContactEmail function did.
+    // Since these queries should be quick & often only one should be needed this is a simpler alternative
+    // to constructing a UNION via the api.
+    $request['params'][$defaults['search_field_fallback']] = $request['params'][$defaults['search_field']];
+    if ($request['params']['options']['sort'] === $defaults['search_field']) {
+      // The way indexing works here is that the order by field will be chosen in preference to the
+      // filter field. This can result in really bad performance so use the filter field for the sort.
+      // See https://github.com/civicrm/civicrm-core/pull/16993 for performance test results.
+      $request['params']['options']['sort'] = $defaults['search_field_fallback'];
+    }
+    // Exclude anything returned from the previous query since we are looking for additional rows in this
+    // second query.
+    $request['params'][$defaults['search_field']] = ['NOT LIKE' => $request['params'][$defaults['search_field_fallback']]['LIKE']];
+    $request['params']['options']['limit'] -= $result['count'];
+    $result2 = civicrm_api3($entity, 'get', $request['params']);
+    $result['values'] = array_merge($result['values'], $result2['values']);
+    $result['count'] = count($result['values']);
+  }
+  else {
+    // Re-index to sequential = 0.
+    $result['values'] = array_merge($result['values']);
+  }
 
   // Hey api, would you like to format the output?
   $fnName = "_civicrm_api3_{$entity}_getlist_output";
@@ -98,7 +124,7 @@ function _civicrm_api3_generic_getList_defaults($entity, &$request, $apiDefaults
   $request += $apiDefaults + $defaults;
   // Default api params
   $params = [
-    'sequential' => 1,
+    'sequential' => 0,
     'options' => [],
   ];
   // When searching e.g. autocomplete