From 815facd4d58fff895ae9b969f23d68b5baef217a Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 8 Feb 2022 18:35:33 -0500 Subject: [PATCH] PseudoConstant - Allow multi-dimensionaly arrays to be returned from option callbacks Before: Only flat arrays could be returned by a pseudoconstant callback fn. After: Callbacks can return arrays with id/name/label/abbr. --- CRM/Core/BAO/Translation.php | 29 +++++---------------------- CRM/Core/PseudoConstant.php | 38 +++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/CRM/Core/BAO/Translation.php b/CRM/Core/BAO/Translation.php index 858a75bcb2..7d3d0c29b3 100644 --- a/CRM/Core/BAO/Translation.php +++ b/CRM/Core/BAO/Translation.php @@ -21,14 +21,13 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements \Civi /** * Get a list of valid statuses for translated-strings. * - * @return string[] + * @return array[] */ - public static function getStatuses($context = NULL) { - $options = [ + public static function getStatuses() { + return [ ['id' => 1, 'name' => 'active', 'label' => ts('Active')], ['id' => 2, 'name' => 'draft', 'label' => ts('Draft')], ]; - return self::formatPsuedoconstant($context, $options); } /** @@ -64,24 +63,6 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements \Civi return Civi::$statics[__CLASS__]['allFields']; } - /** - * Given a constant list of of id/name/label options, convert to the - * format required by pseudoconstant. - * - * @param string|null $context - * @param array $options - * List of options, each as a record of id+name+label. - * Ex: [['id' => 123, 'name' => 'foo_bar', 'label' => 'Foo Bar']] - * - * @return array|false - */ - private static function formatPsuedoconstant($context, array $options) { - // https://docs.civicrm.org/dev/en/latest/framework/pseudoconstant/#context - $key = ($context === 'match') ? 'name' : 'id'; - $value = ($context === 'validate') ? 'name' : 'label'; - return array_combine(array_column($options, $key), array_column($options, $value)); - } - /** * @return array * List of data fields to translate, organized by table and column. @@ -112,12 +93,12 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements \Civi * @param \Civi\Api4\Event\ValidateValuesEvent $e */ public static function self_civi_api4_validate(\Civi\Api4\Event\ValidateValuesEvent $e) { - $statuses = self::getStatuses('validate'); + $statuses = array_column(self::getStatuses(), 'id'); $dataTypes = [CRM_Utils_Type::T_STRING, CRM_Utils_Type::T_TEXT, CRM_Utils_Type::T_LONGTEXT]; $htmlTypes = ['Text', 'TextArea', 'RichTextEditor', '']; foreach ($e->records as $r => $record) { - if (array_key_exists('status_id', $record) && !isset($statuses[$record['status_id']])) { + if (array_key_exists('status_id', $record) && !in_array($record['status_id'], $statuses)) { $e->addError($r, 'status_id', 'invalid', ts('Invalid status')); } diff --git a/CRM/Core/PseudoConstant.php b/CRM/Core/PseudoConstant.php index 17d614da87..c24d734fca 100644 --- a/CRM/Core/PseudoConstant.php +++ b/CRM/Core/PseudoConstant.php @@ -225,13 +225,9 @@ class CRM_Core_PseudoConstant { // if callback is specified.. if (!empty($pseudoconstant['callback'])) { $fieldOptions = call_user_func(Civi\Core\Resolver::singleton()->get($pseudoconstant['callback']), $context, $params); + $fieldOptions = self::formatArrayOptions($context, $fieldOptions); //CRM-18223: Allow additions to field options via hook. CRM_Utils_Hook::fieldOptions($entity, $fieldName, $fieldOptions, $params); - if ($context === 'validate') { - // This mode requires machine names and key/value pairs don't have a name, so - // use key for name. (labels are translatable so don't make suitable machine names) - return array_combine(array_keys($fieldOptions), array_keys($fieldOptions)); - } return $fieldOptions; } @@ -1590,4 +1586,36 @@ WHERE id = %1 return $output; } + /** + * Convert multidimensional option list to flat array, if necessary + * + * Detect if an array of options is simple key/value pairs or a multidimensional array. + * If the latter, convert to a flat array, as determined by $context. + * + * @param string|null $context + * See https://docs.civicrm.org/dev/en/latest/framework/pseudoconstant/#context + * @param array $options + * List of options, each as a record of id+name+label. + * Ex: [['id' => 123, 'name' => 'foo_bar', 'label' => 'Foo Bar']] + */ + private static function formatArrayOptions($context, array &$options) { + // Already flat; return keys/values according to context + if (!isset($options[0]) || !is_array($options[0])) { + // For validate context, machine names are expected in place of labels. + // A flat array has no names so use the ids for both key and value. + return $context === 'validate' ? + array_combine(array_keys($options), array_keys($options)) : + $options; + } + $result = []; + $key = ($context === 'match') ? 'name' : 'id'; + $value = ($context === 'validate') ? 'name' : (($context === 'abbreviate') ? 'abbr' : 'label'); + foreach ($options as $option) { + // Some fallbacks in case the array is missing a 'name' or 'label' or 'abbr' + $id = $option[$key] ?? $option['id'] ?? $option['name']; + $result[$id] = $option[$value] ?? $option['label'] ?? $option['name']; + } + return $result; + } + } -- 2.25.1