APIv4 - Export option groups along with custom fields
authorColeman Watts <coleman@civicrm.org>
Fri, 13 May 2022 01:35:30 +0000 (21:35 -0400)
committerColeman Watts <coleman@civicrm.org>
Tue, 17 May 2022 13:50:30 +0000 (09:50 -0400)
Civi/Api4/Generic/ExportAction.php
tests/phpunit/api/v4/Custom/ExportCustomGroupTest.php [new file with mode: 0644]

index 3fc028dacb956efbad6eaff05face340cac50413..aed9e322a42bb27b9dbb1278bb35078612c82fbc 100644 (file)
@@ -92,6 +92,10 @@ class ExportAction extends AbstractAction {
       elseif (empty($field['fk_entity'])) {
         $select[] = $field['name'];
       }
+      // Needed for exporting the option group for a custom field
+      if ($entityType === 'CustomField' && ($field['fk_entity'] ?? NULL) === 'OptionGroup') {
+        $select[] = $field['name'];
+      }
     }
     $record = civicrm_api4($entityType, 'get', [
       'checkPermissions' => $this->checkPermissions,
@@ -131,6 +135,17 @@ class ExportAction extends AbstractAction {
     $name = ($parentName ?? '') . $entityType . '_' . ($record['name'] ?? count($this->exportedEntities[$entityType]));
     // Ensure safe characters, max length
     $name = \CRM_Utils_String::munge($name, '_', 127);
+    // Include option group with custom field
+    if ($entityType === 'CustomField') {
+      if (
+        !empty($record['option_group_id.name']) &&
+        // Sometimes fields share an option group; only export it once.
+        empty($this->exportedEntities['OptionGroup'][$record['option_group_id']])
+      ) {
+        $this->exportRecord('OptionGroup', $record['option_group_id'], $result);
+      }
+      unset($record['option_group_id']);
+    }
     $result[] = [
       'name' => $name,
       'entity' => $entityType,
@@ -151,6 +166,10 @@ class ExportAction extends AbstractAction {
       $references = [];
       foreach ($dao->findReferences() as $reference) {
         $refEntity = \CRM_Utils_Array::first($reference::fields())['entity'] ?? '';
+        // Custom fields don't really "belong" to option groups despite the reference
+        if ($refEntity === 'CustomField' && $entityType === 'OptionGroup') {
+          continue;
+        }
         // Limit references by domain
         if (property_exists($reference, 'domain_id')) {
           if (!isset($reference->domain_id)) {
diff --git a/tests/phpunit/api/v4/Custom/ExportCustomGroupTest.php b/tests/phpunit/api/v4/Custom/ExportCustomGroupTest.php
new file mode 100644 (file)
index 0000000..e12a887
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace api\v4\Custom;
+
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+
+/**
+ * @group headless
+ */
+class ExportCustomGroupTest extends CustomTestBase {
+
+  public function testExportCustomGroupWithFieldOptions() {
+    $optionValues = ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'];
+
+    $customGroup = CustomGroup::create(FALSE)
+      ->addValue('title', 'exportTest')
+      ->addValue('extends', 'Individual')
+      ->addChain('field1', CustomField::create()
+        ->addValue('custom_group_id', '$id')
+        ->addValue('option_values', $optionValues)
+        ->addValue('label', 'Color')
+        ->addValue('html_type', 'Select'), 0
+      )->addChain('field2', CustomField::create()
+        ->addValue('custom_group_id', '$id')
+        ->addValue('data_type', 'Boolean')
+        ->addValue('label', 'On Off')
+        ->addValue('html_type', 'CheckBox'), 0
+      )->execute()->single();
+
+    // Add a 3rd fields that shares the same option group as field1
+    CustomField::create(FALSE)
+      ->addValue('custom_group_id', $customGroup['id'])
+      ->addValue('label', 'Color2')
+      ->addValue('html_type', 'Select')
+      ->addValue('option_group_id', $customGroup['field1']['option_group_id'])
+      ->execute();
+
+    $export = CustomGroup::export(FALSE)
+      ->setId($customGroup['id'])
+      ->execute();
+
+    // 1 custom group + 3 fields + 1 option group + 3 options
+    $this->assertCount(8, $export);
+    // 2 fields share an option group
+    $this->assertEquals($export[5]['params']['values']['option_group_id.name'], $export[7]['params']['values']['option_group_id.name']);
+    // Option group name matches
+    $this->assertEquals($export[5]['params']['values']['option_group_id.name'], $export[1]['params']['values']['name']);
+    // Should be only name, not id
+    $this->assertArrayNotHasKey('option_group_id', $export[5]['params']['values']);
+    $this->assertArrayNotHasKey('option_group_id.name', $export[6]['params']['values']);
+    $this->assertArrayNotHasKey('option_group_id', $export[6]['params']['values']);
+    $this->assertArrayNotHasKey('option_values', $export[6]['params']['values']);
+  }
+
+}