From e01b30d70a0f19839d0c0f0e2f2213d85f4fea0b Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 25 Feb 2022 10:42:29 -0500 Subject: [PATCH] APIv4 - Add entity metadata about class args The CustomValue API is a virtual API, where multiple entities all get routed to the same class if they share the prefix "Custom_", and pass a class arg to the php factory functions e.g. `CustomValue::get('MyCustomGroup')`. Instead of hard-coding this idea into the now it's part of the entity metadata so that other APIs, notaby ECK, can use a similar pattern. --- Civi/Api4/Entity.php | 5 +++++ Civi/Api4/Provider/CustomEntityProvider.php | 15 ++++++++------- ang/api4Explorer/Explorer.js | 12 ++++++------ tests/phpunit/api/v4/Action/CustomValueTest.php | 12 ++++++++++++ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Civi/Api4/Entity.php b/Civi/Api4/Entity.php index bc6b18bc6d..915e739d50 100644 --- a/Civi/Api4/Entity.php +++ b/Civi/Api4/Entity.php @@ -126,6 +126,11 @@ class Entity extends Generic\AbstractEntity { 'data_type' => 'String', 'description' => 'PHP class name', ], + [ + 'name' => 'class_args', + 'data_type' => 'Array', + 'description' => 'Arguments needed by php action factory functions (used when multiple entities share a class, e.g. CustomValue).', + ], [ 'name' => 'bridge', 'data_type' => 'Array', diff --git a/Civi/Api4/Provider/CustomEntityProvider.php b/Civi/Api4/Provider/CustomEntityProvider.php index 9acb55dbbb..11b324cd7f 100644 --- a/Civi/Api4/Provider/CustomEntityProvider.php +++ b/Civi/Api4/Provider/CustomEntityProvider.php @@ -29,27 +29,28 @@ class CustomEntityProvider { ->toSQL(); $group = \CRM_Core_DAO::executeQuery($select); while ($group->fetch()) { - $fieldName = 'Custom_' . $group->name; + $entityName = 'Custom_' . $group->name; $baseEntity = CoreUtil::getApiClass(CustomGroupJoinable::getEntityFromExtends($group->extends)); - $e->entities[$fieldName] = [ - 'name' => $fieldName, + $e->entities[$entityName] = [ + 'name' => $entityName, 'title' => $group->title, 'title_plural' => $group->title, 'table_name' => $group->table_name, + 'class_args' => [$group->name], 'description' => ts('Custom group for %1', [1 => $baseEntity::getInfo()['title_plural']]), 'paths' => [ 'view' => "civicrm/contact/view/cd?reset=1&gid={$group->id}&recId=[id]&multiRecordDisplay=single", ], ] + $baseInfo; if (!empty($group->icon)) { - $e->entities[$fieldName]['icon'] = $group->icon; + $e->entities[$entityName]['icon'] = $group->icon; } if (!empty($group->help_pre)) { - $e->entities[$fieldName]['comment'] = self::plainTextify($group->help_pre); + $e->entities[$entityName]['comment'] = self::plainTextify($group->help_pre); } if (!empty($group->help_post)) { - $pre = empty($e->entities[$fieldName]['comment']) ? '' : $e->entities[$fieldName]['comment'] . "\n\n"; - $e->entities[$fieldName]['comment'] = $pre . self::plainTextify($group->help_post); + $pre = empty($e->entities[$entityName]['comment']) ? '' : $e->entities[$entityName]['comment'] . "\n\n"; + $e->entities[$entityName]['comment'] = $pre . self::plainTextify($group->help_post); } } } diff --git a/ang/api4Explorer/Explorer.js b/ang/api4Explorer/Explorer.js index 151aa1c8d4..cedc623e38 100644 --- a/ang/api4Explorer/Explorer.js +++ b/ang/api4Explorer/Explorer.js @@ -688,13 +688,14 @@ var code = {}, entity = $scope.entity, action = $scope.action, + args = getEntity(entity).class_args || [], params = getParams(), index = isInt($scope.index) ? +$scope.index : parseYaml($scope.index), result = 'result'; if ($scope.entity && $scope.action) { delete params.debug; if (action.slice(0, 3) === 'get') { - result = entity.substr(0, 7) === 'Custom_' ? _.camelCase(entity.substr(7)) : entity; + result = args[0] ? _.camelCase(args[0]) : entity; result = lcfirst(action.replace(/s$/, '').slice(3) || result); } var results = lcfirst(_.isNumber(index) ? result : pluralize(result)), @@ -862,12 +863,11 @@ arrayParams = ['groupBy', 'records'], newLine = "\n" + _.repeat(' ', indent), code = '\\' + info.class + '::' + action + '(', - perm = params.checkPermissions === false ? 'FALSE' : ''; - if (entity.substr(0, 7) !== 'Custom_') { - code += perm + ')'; - } else { - code += "'" + entity.substr(7) + "'" + (perm ? ', ' : '') + perm + ")"; + args = _.cloneDeep(info.class_args || []); + if (params.checkPermissions === false) { + args.push(false); } + code += _.map(args, phpFormat).join(', ') + ')'; _.each(params, function(param, key) { var val = ''; if (typeof objectParams[key] !== 'undefined' && key !== 'chain') { diff --git a/tests/phpunit/api/v4/Action/CustomValueTest.php b/tests/phpunit/api/v4/Action/CustomValueTest.php index 68e510d06d..337cc3c590 100644 --- a/tests/phpunit/api/v4/Action/CustomValueTest.php +++ b/tests/phpunit/api/v4/Action/CustomValueTest.php @@ -23,6 +23,7 @@ use Civi\Api4\CustomField; use Civi\Api4\CustomGroup; use Civi\Api4\CustomValue; use Civi\Api4\Contact; +use Civi\Api4\Entity; /** * @group headless @@ -79,6 +80,17 @@ class CustomValueTest extends BaseCustomValueTest { ->execute() ->first()['id']; + // Ensure virtual api entity has been created + $entity = Entity::get(FALSE) + ->addWhere('name', '=', "Custom_$group") + ->execute()->single(); + $this->assertEquals(['CustomValue'], $entity['type']); + $this->assertEquals(['id'], $entity['primary_key']); + $this->assertEquals($customGroup['table_name'], $entity['table_name']); + $this->assertEquals('Civi\Api4\CustomValue', $entity['class']); + $this->assertEquals([$group], $entity['class_args']); + $this->assertEquals('secondary', $entity['searchable']); + // Retrieve and check the fields of CustomValue = Custom_$group $fields = CustomValue::getFields($group)->setLoadOptions(TRUE)->setCheckPermissions(FALSE)->execute(); $expectedResult = [ -- 2.25.1