Autocomplete - Fix search-by-id for entities without a numeric id
authorcolemanw <coleman@civicrm.org>
Mon, 25 Sep 2023 14:02:21 +0000 (10:02 -0400)
committercolemanw <coleman@civicrm.org>
Mon, 25 Sep 2023 14:49:18 +0000 (10:49 -0400)
Fixes dev/core#4560 and the intermittent failure of AfformGetTest.testAfformAutocomplete

Civi/Api4/EntitySet.php
Civi/Api4/Generic/AutocompleteAction.php
ext/afform/core/tests/phpunit/Civi/Afform/AfformGetTest.php
tests/phpunit/api/v4/Entity/ConformanceTest.php

index b77fdae15a2494e6f2d935d0d7df2bdd8e9833fe..f25208137c289abbf3771b6f13bea53f6527fb9f 100644 (file)
@@ -39,6 +39,7 @@ class EntitySet extends Generic\AbstractEntity {
   }
 
   /**
+   * This isn't a "real" entity and doesn't have any fields
    * @return \Civi\Api4\Generic\BasicGetFieldsAction
    */
   public static function getFields($checkPermissions = TRUE) {
@@ -61,4 +62,11 @@ class EntitySet extends Generic\AbstractEntity {
     return $plural ? ts('Entity Sets') : ts('Entity Set');
   }
 
+  public static function getInfo(): array {
+    $info = parent::getInfo();
+    // This isn't a "real" entity and doesn't have any fields, so no primary key
+    $info['primary_key'] = [];
+    return $info;
+  }
+
 }
index 313f6a67ab3547ab1cefd6f509d9a956e03184b3..73ce6e9d4d00900d080899f3cf8097036aa62aa1 100644 (file)
@@ -149,23 +149,29 @@ class AutocompleteAction extends AbstractAction {
     else {
       // Default search and sort field
       $labelField = $this->display['settings']['columns'][0]['key'];
-      $idField = CoreUtil::getIdFieldName($this->savedSearch['api_entity']);
+      $primaryKeys = CoreUtil::getInfoItem($this->savedSearch['api_entity'], 'primary_key');
       $this->display['settings'] += [
         'sort' => [$labelField, 'ASC'],
       ];
       // Always search on the first line of the display
       $searchFields = [$labelField];
-      // If input is an integer, search by id
-      $numericInput = $this->page == 1 && \CRM_Utils_Rule::positiveInteger($this->input);
-      if ($numericInput) {
-        $searchFields = [$idField];
+      // If input is an integer...
+      $searchById = \CRM_Utils_Rule::positiveInteger($this->input) &&
+        // ...and there is exactly one primary key (e.g. EntitySet has zero, others might have compound keys)
+        count($primaryKeys) === 1 &&
+        // ...and the primary key field is type Integer (e.g. Afform.name is a String)
+        ($this->getField($primaryKeys[0])['data_type'] ?? NULL) === 'Integer';
+      // ...then search by primary key on first page
+      $initialSearchById = $searchById && $this->page == 1;
+      if ($initialSearchById) {
+        $searchFields = $primaryKeys;
       }
-      // For subsequent pages when searching numeric input
-      elseif ($this->page > 1 && \CRM_Utils_Rule::positiveInteger($this->input)) {
+      // For subsequent pages when searching by id, subtract the "extra" first page
+      elseif ($searchById && $this->page > 1) {
         $this->page -= 1;
       }
       // If first line uses a rewrite, search on those fields too
-      if (!empty($this->display['settings']['columns'][0]['rewrite'])) {
+      if (!$initialSearchById && !empty($this->display['settings']['columns'][0]['rewrite'])) {
         $searchFields = array_merge($searchFields, $this->getTokens($this->display['settings']['columns'][0]['rewrite']));
       }
       $this->display['settings']['limit'] = $this->display['settings']['limit'] ?? \Civi::settings()->get('search_autocomplete_count');
@@ -198,7 +204,7 @@ class AutocompleteAction extends AbstractAction {
       $result[] = $item;
     }
     $result->setCountMatched($apiResult->count());
-    if (!empty($numericInput)) {
+    if (!empty($initialSearchById)) {
       // Trigger "more results" after searching by exact id
       $result->setCountMatched($apiResult->count() + 1);
     }
index f93fea9bdf38e0ab1745b7dc1509610724f0b53b..fa5766e857bd03cf5419ebe97adda40ac6cb8ebf 100644 (file)
@@ -72,7 +72,9 @@ class AfformGetTest extends \PHPUnit\Framework\TestCase implements HeadlessInter
   }
 
   public function testAfformAutocomplete(): void {
-    $title = uniqid();
+    // Use a numeric title to test that the "search by id" feature
+    // doesn't kick in for Afforms (which don't have a numeric "id")
+    $title = (string) rand(1000, 999999);
     Afform::create()
       ->addValue('name', $this->formName)
       ->addValue('title', $title)
index 75fa3b9ca37b4df159edbe6e4d76c1916d8f0fde..da6725ba44d5e285e276296fbc534136a8643d07 100644 (file)
@@ -169,7 +169,6 @@ class ConformanceTest extends Api4TestBase implements HookInterface {
     $this->assertNotEmpty($info['type']);
     $this->assertNotEmpty($info['description']);
     $this->assertIsArray($info['primary_key']);
-    $this->assertNotEmpty($info['primary_key']);
     $this->assertMatchesRegularExpression(';^\d\.\d+$;', $info['since']);
     $this->assertContains($info['searchable'], ['primary', 'secondary', 'bridge', 'none']);
   }