SearchKit - Prevent errors trying to orderBy nonaggregated columns when using groupBy
authorColeman Watts <coleman@civicrm.org>
Tue, 19 Apr 2022 14:07:13 +0000 (10:07 -0400)
committerColeman Watts <coleman@civicrm.org>
Tue, 19 Apr 2022 21:27:46 +0000 (17:27 -0400)
ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchRunTest.php

index 6d63a56d8ff15d9e18d2032c34ee16421f342623..63a59d9da4e4fc1c8aa9c4ad01fa43a6480a7df2 100644 (file)
@@ -854,13 +854,13 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction {
     }
 
     $defaultSort = $this->display['settings']['sort'] ?? [];
-    $currentSort = $this->sort;
+    $currentSort = [];
 
-    // Verify requested sort corresponds to sortable columns
+    // Add requested sort after verifying it corresponds to sortable columns
     foreach ($this->sort as $item) {
       $column = array_column($this->display['settings']['columns'], NULL, 'key')[$item[0]] ?? NULL;
-      if (!$column || (isset($column['sortable']) && !$column['sortable'])) {
-        $currentSort = NULL;
+      if ($column && !(isset($column['sortable']) && !$column['sortable'])) {
+        $currentSort[] = $item;
       }
     }
 
@@ -870,6 +870,10 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction {
       if ($item[0] === 'RAND()' && isset($this->seed)) {
         $item[0] = 'RAND(' . $this->seed . ')';
       }
+      // Prevent errors trying to orderBy nonaggregated columns when using groupBy
+      if ($this->canAggregate($item[0])) {
+        continue;
+      }
       $orderBy[$item[0]] = $item[1];
     }
     return $orderBy;
index 0f7cc73637c972ff51c625029434bcb11e6aa0fb..2b8a2c02c67e9a2885092d6f31aeeaf3a3491dff 100644 (file)
@@ -1040,4 +1040,43 @@ class SearchRunTest extends \PHPUnit\Framework\TestCase implements HeadlessInter
     $this->assertEquals('Contact', $result[0]['columns'][0]['val']);
   }
 
+  public function testGroupByContactType(): void {
+    $source = uniqid(__FUNCTION__);
+    $sampleData = [
+      ['contact_type' => 'Individual'],
+      ['contact_type' => 'Individual'],
+      ['contact_type' => 'Individual'],
+      ['contact_type' => 'Organization'],
+      ['contact_type' => 'Organization'],
+      ['contact_type' => 'Household'],
+    ];
+    Contact::save(FALSE)
+      ->addDefault('source', $source)
+      ->setRecords($sampleData)
+      ->execute();
+
+    $params = [
+      'checkPermissions' => FALSE,
+      'return' => 'page:1',
+      'savedSearch' => [
+        'api_entity' => 'Contact',
+        'api_params' => [
+          'version' => 4,
+          'select' => ['contact_type:label', 'COUNT(id) AS COUNT_id'],
+          'where' => [['source', '=', $source]],
+          'groupBy' => ['contact_type'],
+        ],
+      ],
+      'display' => NULL,
+      'afform' => NULL,
+    ];
+
+    $result = civicrm_api4('SearchDisplay', 'run', $params);
+    $this->assertCount(3, $result);
+    $data = array_column(array_column((array) $result, 'data'), 'COUNT_id', 'contact_type:label');
+    $this->assertEquals(3, $data['Individual']);
+    $this->assertEquals(2, $data['Organization']);
+    $this->assertEquals(1, $data['Household']);
+  }
+
 }