From 77286ab8678cc94d19f7ce68a221ba506b0b0dff Mon Sep 17 00:00:00 2001 From: colemanw Date: Sun, 10 Sep 2023 18:39:31 -0400 Subject: [PATCH] APIv4 - Support output_formatters with aggregated columns --- Civi/Api4/Utils/FormattingUtil.php | 37 ++++++++++++++----- .../phpunit/api/v4/Action/EntityFileTest.php | 30 +++++++++++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/Civi/Api4/Utils/FormattingUtil.php b/Civi/Api4/Utils/FormattingUtil.php index 4a63e4db41..0376bb044e 100644 --- a/Civi/Api4/Utils/FormattingUtil.php +++ b/Civi/Api4/Utils/FormattingUtil.php @@ -237,17 +237,17 @@ class FormattingUtil { continue; } $fieldExpr = SqlExpression::convert($selectAliases[$key] ?? $key); - $fieldName = \CRM_Utils_Array::first($fieldExpr->getFields() ?? ''); + $fieldName = \CRM_Utils_Array::first($fieldExpr->getFields()); $baseName = $fieldName ? \CRM_Utils_Array::first(explode(':', $fieldName)) : NULL; $field = $fields[$fieldName] ?? $fields[$baseName] ?? NULL; $dataType = $field['data_type'] ?? ($fieldName == 'id' ? 'Integer' : NULL); - // Allow Sql Functions to do alter the value and/or $dataType + // Allow Sql Functions to alter the value and/or $dataType if (method_exists($fieldExpr, 'formatOutputValue') && is_string($value)) { $fieldExpr->formatOutputValue($dataType, $result, $key); $value = $result[$key]; } if (!empty($field['output_formatters'])) { - self::applyFormatters($result, $fieldName, $field, $value); + self::applyFormatters($result, $fieldExpr, $field, $value); $dataType = NULL; } // Evaluate pseudoconstant suffixes @@ -345,15 +345,34 @@ class FormattingUtil { * Apply a field's output_formatters callback functions * * @param array $result - * @param string $fieldPath - * @param array $field + * @param \Civi\Api4\Query\SqlExpression $fieldExpr + * @param array $fieldDefn * @param mixed $value */ - private static function applyFormatters(array $result, string $fieldPath, array $field, &$value) { - $row = self::filterByPath($result, $fieldPath, $field['name']); + private static function applyFormatters(array $result, SqlExpression $fieldExpr, array $fieldDefn, &$value): void { + $fieldPath = \CRM_Utils_Array::first($fieldExpr->getFields()); + $row = self::filterByPath($result, $fieldPath, $fieldDefn['name']); + + // For aggregated array data, apply the formatter to each item + if (is_array($value) && $fieldExpr->getType() === 'SqlFunction' && $fieldExpr::getCategory() === 'aggregate') { + foreach ($value as $index => &$val) { + $subRow = $row; + foreach ($row as $rowKey => $rowValue) { + if (is_array($rowValue) && array_key_exists($index, $rowValue)) { + $subRow[$rowKey] = $rowValue[$index]; + } + } + self::applyFormatter($fieldDefn, $subRow, $val); + } + } + else { + self::applyFormatter($fieldDefn, $row, $value); + } + } - foreach ($field['output_formatters'] as $formatter) { - $formatter($value, $row, $field); + private static function applyFormatter(array $fieldDefn, array $row, &$value): void { + foreach ($fieldDefn['output_formatters'] as $formatter) { + $formatter($value, $row, $fieldDefn); } } diff --git a/tests/phpunit/api/v4/Action/EntityFileTest.php b/tests/phpunit/api/v4/Action/EntityFileTest.php index 54777e9802..9df5d3b11e 100644 --- a/tests/phpunit/api/v4/Action/EntityFileTest.php +++ b/tests/phpunit/api/v4/Action/EntityFileTest.php @@ -20,6 +20,7 @@ namespace api\v4\Action; use api\v4\Api4TestBase; +use Civi\Api4\Activity; use Civi\Api4\EntityFile; use Civi\Api4\File; use Civi\Api4\Note; @@ -98,4 +99,33 @@ class EntityFileTest extends Api4TestBase implements TransactionalInterface, Hoo $this->assertStringContainsString("id=$file[3]&eid=$note[3]&fcs=", $allowedNotes[$note[3]]['file.url']); } + public function testGetAggregateFileFields() { + $activity = $this->createTestRecord('Activity'); + + foreach (['text/plain' => 'txt', 'image/png' => 'png', 'image/jpg' => 'jpg'] as $mimeType => $ext) { + // FIXME: Use api4 when available + civicrm_api3('Attachment', 'create', [ + 'entity_table' => 'civicrm_activity', + 'entity_id' => $activity['id'], + 'name' => 'test_file.' . $ext, + 'mime_type' => $mimeType, + 'content' => 'hello', + ])['id']; + } + + $get = Activity::get(FALSE) + ->addWhere('id', '=', $activity['id']) + ->addJoin('File AS file', 'LEFT', 'EntityFile', ['file.entity_id', '=', 'id'], ['file.entity_table', '=', '"civicrm_activity"']) + ->addGroupBy('id') + ->addSelect('GROUP_CONCAT(UNIQUE file.file_name) AS aggregate_file_name') + ->addSelect('GROUP_CONCAT(UNIQUE file.url) AS aggregate_url') + ->addSelect('GROUP_CONCAT(UNIQUE file.icon) AS aggregate_icon') + ->execute()->single(); + + $this->assertCount(3, $get['aggregate_url']); + $this->assertCount(3, $get['aggregate_icon']); + $this->assertEquals(['test_file.txt', 'test_file.png', 'test_file.jpg'], $get['aggregate_file_name']); + $this->assertEquals(['fa-file-text-o', 'fa-file-image-o', 'fa-file-image-o'], $get['aggregate_icon']); + } + } -- 2.25.1