APIv4 - Prevent fatal errors when getOptions returns an empty array
authorcolemanw <coleman@civicrm.org>
Thu, 12 Oct 2023 14:06:01 +0000 (10:06 -0400)
committercolemanw <coleman@civicrm.org>
Thu, 12 Oct 2023 15:13:15 +0000 (11:13 -0400)
Civi/Api4/Generic/BasicGetFieldsAction.php
Civi/Api4/Service/Spec/Provider/EntityTagFilterSpecProvider.php
Civi/Api4/Service/Spec/SpecFormatter.php
tests/phpunit/api/v4/Action/GetFieldsTest.php
tests/phpunit/api/v4/Custom/BasicCustomFieldTest.php

index 895701f2adb7017c427028e91eaebecf13db361a..2dc62f8cb082d0d7bb478b537872dc27ffcfd425 100644 (file)
@@ -138,7 +138,7 @@ class BasicGetFieldsAction extends BasicGetAction {
       if (array_key_exists('label', $fieldDefaults)) {
         $field['label'] = $field['label'] ?? $field['title'] ?? $field['name'];
       }
-      if (!empty($field['options']) && is_array($field['options']) && empty($field['suffixes']) && array_key_exists('suffixes', $field)) {
+      if (isset($field['options']) && is_array($field['options']) && empty($field['suffixes']) && array_key_exists('suffixes', $field)) {
         $this->setFieldSuffixes($field);
       }
       if (isset($defaults['options'])) {
@@ -156,13 +156,14 @@ class BasicGetFieldsAction extends BasicGetAction {
    * @param array $field
    */
   private function formatOptionList(&$field) {
-    if (empty($field['options'])) {
+    $optionsExist = isset($field['options']) && is_array($field['options']);
+    if (!isset($field['options'])) {
       $field['options'] = !empty($field['pseudoconstant']);
     }
     if (!empty($field['pseudoconstant']['optionGroupName'])) {
       $field['suffixes'] = CoreUtil::getOptionValueFields($field['pseudoconstant']['optionGroupName']);
     }
-    if (!$this->loadOptions || !$field['options']) {
+    if (!$this->loadOptions || (!$optionsExist && empty($field['pseudoconstant']))) {
       $field['options'] = (bool) $field['options'];
       return;
     }
index bdc05f6810aee19a0f58b006c0f7167c08a3cb2a..ac900b7299a52858dc439241f16dfe19d0e6ab33 100644 (file)
@@ -74,7 +74,7 @@ class EntityTagFilterSpecProvider extends \Civi\Core\Service\AutoService impleme
         $value = array_unique(array_merge($value, $tagTree[$tagID]));
       }
     }
-    $tags = \CRM_Utils_Type::validate(implode(',', $value), 'CommaSeparatedIntegers');
+    $tags = $value ? \CRM_Utils_Type::validate(implode(',', $value), 'CommaSeparatedIntegers') : '0';
     return "$fieldAlias $operator (SELECT entity_id FROM `civicrm_entity_tag` WHERE entity_table = '$tableName' AND tag_id IN ($tags))";
   }
 
index 622a11178bbae6aa610ea76807c6019aeaa708e5..2305f630a473483520d99c3f20fe785bda0113a6 100644 (file)
@@ -164,7 +164,7 @@ class SpecFormatter {
     $bao = CoreUtil::getBAOFromApiName($spec->getEntity());
     $optionLabels = $bao::buildOptions($fieldName, NULL, $values);
 
-    if (!is_array($optionLabels) || !$optionLabels) {
+    if (!is_array($optionLabels)) {
       $options = FALSE;
     }
     else {
index 8a3bea82bee4db637da8f1282bfbc4902c149b21..1addb7f5e6ca5b9642e7b0ca7abd99b757172130 100644 (file)
@@ -30,6 +30,7 @@ use Civi\Api4\CustomGroup;
 use Civi\Api4\Email;
 use Civi\Api4\EntityTag;
 use Civi\Api4\OptionValue;
+use Civi\Api4\Tag;
 use Civi\Api4\UserJob;
 use Civi\Api4\Utils\CoreUtil;
 use Civi\Test\TransactionalInterface;
@@ -232,6 +233,20 @@ class GetFieldsTest extends Api4TestBase implements TransactionalInterface {
     $this->assertContains('SavedSearch', $tagFields['entity_id']['dfk_entities']);
   }
 
+  public function testEmptyOptionListIsReturnedAsAnArray(): void {
+    Tag::delete(FALSE)
+      ->addWhere('used_for', '=', 'civicrm_activity')
+      ->execute();
+    $field = EntityTag::getFields(FALSE)
+      ->addValue('entity_table', 'civicrm_activity')
+      ->addWhere('name', '=', 'tag_id')
+      ->setLoadOptions(TRUE)
+      ->execute()->single();
+    // There are no tags for Activity but it should still be returned as an array
+    $this->assertIsArray($field['options']);
+    $this->assertEmpty($field['options']);
+  }
+
   public function testFiltersAreReturned(): void {
     $field = Contact::getFields(FALSE)
       ->addWhere('name', '=', 'employer_id')
index de375c0333ea054d52f5d5555618e52c46c2b4bf..f70f7c5481bc80dfc4fcb55a0f121f67ed64d38f 100644 (file)
@@ -584,7 +584,7 @@ class BasicCustomFieldTest extends CustomTestBase {
       ->addWhere('name', '=', 'extends_entity_column_id')
       ->addValue('extends', 'Contact')
       ->execute()->first();
-    $this->assertFalse($fieldFilteredByContact['options']);
+    $this->assertEquals([], $fieldFilteredByContact['options']);
   }
 
   public function testExtendsMetadata(): void {