Before: Impossible to export a profile with custom fields as a managed entity because of the dependence on custom ids as the fieldname.
After: Uses APIv4-style names when exporting custom fields for profiles, which makes them portable.
return CRM_Utils_Array::collect('title', $fields);
}
+ /**
+ * Get pseudoconstant list for `field_name`
+ *
+ * Includes APIv4-style names for custom fields for portability.
+ *
+ * @return array
+ */
+ public static function getAvailableFieldOptions() {
+ $fields = self::getAvailableFieldsFlat();
+ $fields['formatting'] = ['title' => ts('Formatting')];
+ $options = [];
+ foreach ($fields as $fieldName => $field) {
+ $option = [
+ 'id' => $fieldName,
+ 'name' => $fieldName,
+ 'label' => $field['title'],
+ ];
+ if (!empty($field['custom_group_id']) && !empty($field['id'])) {
+ $groupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $field['custom_group_id']);
+ $fieldName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $field['id']);
+ $option['name'] = "$groupName.$fieldName";
+ }
+ $options[] = $option;
+ }
+ return $options;
+ }
+
/**
* Determine whether the given field_name is valid.
*
*
* Generated from xml/schema/CRM/Core/UFField.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:4d8ac7c59b6f2301cc22fe7966e8fc91)
+ * (GenCodeChecksum:350622e76d9fc3367d201bb3a9c91f18)
*/
/**
'bao' => 'CRM_Core_BAO_UFField',
'localizable' => 0,
'pseudoconstant' => [
- 'callback' => 'CRM_Core_BAO_UFField::getAvailableFieldTitles',
+ 'callback' => 'CRM_Core_BAO_UFField::getAvailableFieldOptions',
],
'add' => '1.1',
],
foreach ($allFields as $field) {
// Use implicit join syntax but only if the fk entity has a `name` field
if (!empty($field['fk_entity']) && array_key_exists('name', $this->getFieldsForExport($field['fk_entity']))) {
+ $select[] = $field['name'];
$select[] = $field['name'] . '.name';
$pseudofields[$field['name'] . '.name'] = $field['name'];
}
// Use pseudoconstant syntax if appropriate
elseif ($this->shouldUsePseudoconstant($entityType, $field)) {
+ $select[] = $field['name'];
$select[] = $field['name'] . ':name';
$pseudofields[$field['name'] . ':name'] = $field['name'];
}
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,
}
// The get api always returns ID, but it should not be included in an export
unset($record['id']);
- // Null fields should not use joins/pseudoconstants
- foreach ($pseudofields as $alias => $fieldName) {
- if (is_null($record[$alias])) {
- unset($record[$alias]);
- $record[$fieldName] = NULL;
- }
- }
// Should references be limited to the current domain?
$limitRefsByDomain = $entityType === 'OptionGroup' && \CRM_Core_OptionGroup::isDomainOptionGroup($record['name']) ? \CRM_Core_BAO_Domain::getDomain()->id : FALSE;
foreach ($allFields as $fieldName => $field) {
) {
$this->exportRecord('OptionGroup', $record['option_group_id'], $result);
}
- unset($record['option_group_id']);
+ }
+ // Don't use joins/pseudoconstants if null or if it has the same value as the original
+ foreach ($pseudofields as $alias => $fieldName) {
+ if (!isset($record[$alias]) || $record[$alias] == ($record[$fieldName] ?? NULL)) {
+ unset($record[$alias]);
+ }
+ else {
+ unset($record[$fieldName]);
+ }
}
$result[] = [
'name' => $name,
return FALSE;
}
$daoName = CoreUtil::getInfoItem($entityType, 'dao');
+ // Exception for Profile.field_name
+ if ($entityType === 'UFField' && $field['name'] === 'field_name') {
+ return TRUE;
+ }
// Options generated by a callback function tend to be stable,
// and the :name property may not be reliable. Use plain value.
if ($daoName && !empty($daoName::getSupportedFields()[$field['name']]['pseudoconstant']['callback'])) {
--- /dev/null
+<?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\CustomGroup;
+use Civi\Api4\UFGroup;
+
+/**
+ * @group headless
+ */
+class CustomProfileFieldTest extends CustomTestBase {
+
+ public function testExportProfileWithCustomFields() {
+ $customGroup = CustomGroup::create(FALSE)
+ ->addValue('title', 'ProfileGroup')
+ ->addValue('extends', 'Individual')
+ ->execute()
+ ->first();
+
+ $custom1 = $this->createTestRecord('CustomField', [
+ 'label' => 'F1',
+ 'custom_group_id' => $customGroup['id'],
+ ]);
+ $custom2 = $this->createTestRecord('CustomField', [
+ 'label' => 'F2',
+ 'custom_group_id' => $customGroup['id'],
+ ]);
+
+ $profile = $this->createTestRecord('UFGroup');
+ $field0 = $this->createTestRecord('UFField', [
+ 'uf_group_id' => $profile['id'],
+ 'field_name' => 'first_name',
+ ]);
+ $field1 = $this->createTestRecord('UFField', [
+ 'uf_group_id' => $profile['id'],
+ 'field_name' => 'custom_' . $custom1['id'],
+ ]);
+ $field2 = $this->createTestRecord('UFField', [
+ 'uf_group_id' => $profile['id'],
+ 'field_name:name' => 'ProfileGroup.F2',
+ ]);
+
+ $export = UFGroup::export(FALSE)
+ ->setId($profile['id'])
+ ->execute();
+
+ $this->assertCount(4, $export);
+
+ $this->assertEquals('UFGroup', $export[0]['entity']);
+ // First Name field should not use pseudoconstant
+ $this->assertEquals('first_name', $export[1]['params']['values']['field_name']);
+ $this->assertArrayNotHasKey('field_name:name', $export[1]['params']['values']);
+ $this->assertEquals('First Name', $export[1]['params']['values']['label']);
+
+ // Custom fields should use pseudoconstants
+ $this->assertEquals('ProfileGroup.F1', $export[2]['params']['values']['field_name:name']);
+ $this->assertArrayNotHasKey('field_name', $export[2]['params']['values']);
+ $this->assertEquals('F1', $export[2]['params']['values']['label']);
+
+ // Custom fields should use pseudoconstants
+ $this->assertEquals('ProfileGroup.F2', $export[3]['params']['values']['field_name:name']);
+ $this->assertArrayNotHasKey('field_name', $export[3]['params']['values']);
+ $this->assertEquals('F2', $export[3]['params']['values']['label']);
+ }
+
+}
$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']);
+ // Field with no options
+ $this->assertNull($export[6]['params']['values']['option_group_id']);
$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']);
}
<required>true</required>
<comment>Name for CiviCRM field which is being exposed for sharing.</comment>
<pseudoconstant>
- <callback>CRM_Core_BAO_UFField::getAvailableFieldTitles</callback>
+ <callback>CRM_Core_BAO_UFField::getAvailableFieldOptions</callback>
</pseudoconstant>
<add>1.1</add>
</field>