diff --git a/CRM/Api4/Page/Api4Explorer.php b/CRM/Api4/Page/Api4Explorer.php index af188f313609..0f1125f5ca2c 100644 --- a/CRM/Api4/Page/Api4Explorer.php +++ b/CRM/Api4/Page/Api4Explorer.php @@ -10,7 +10,6 @@ +--------------------------------------------------------------------+ */ -use Civi\Api4\Service\Schema\Joinable\Joinable; use Civi\Api4\Utils\CoreUtil; /** @@ -23,18 +22,11 @@ class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page { public function run() { $apiDoc = new ReflectionFunction('civicrm_api4'); $groupOptions = civicrm_api4('Group', 'getFields', ['loadOptions' => TRUE, 'select' => ['options', 'name'], 'where' => [['name', 'IN', ['visibility', 'group_type']]]]); - // Don't show n-to-many joins in Explorer - $entityLinks = (array) civicrm_api4('Entity', 'getLinks', [], ['entity' => 'links']); - foreach ($entityLinks as $entity => $links) { - $entityLinks[$entity] = array_filter($links, function($link) { - return $link['joinType'] != Joinable::JOIN_TYPE_ONE_TO_MANY; - }); - } + $vars = [ 'operators' => CoreUtil::getOperators(), 'basePath' => Civi::resources()->getUrl('civicrm'), 'schema' => (array) \Civi\Api4\Entity::get()->setChain(['fields' => ['$name', 'getFields']])->execute(), - 'links' => $entityLinks, 'docs' => \Civi\Api4\Utils\ReflectionUtils::parseDocBlock($apiDoc->getDocComment()), 'functions' => self::getSqlFunctions(), 'groupOptions' => array_column((array) $groupOptions, 'options', 'name'), diff --git a/Civi/Api4/Action/Entity/GetLinks.php b/Civi/Api4/Action/Entity/GetLinks.php index 8ae38eead3d2..e0458b56b599 100644 --- a/Civi/Api4/Action/Entity/GetLinks.php +++ b/Civi/Api4/Action/Entity/GetLinks.php @@ -15,11 +15,15 @@ use Civi\Api4\Utils\CoreUtil; /** - * Get a list of FK links between entities + * Get a list of FK links between entities. + * + * This action is deprecated; the API no longer uses these links to determine available joins. + * @deprecated */ class GetLinks extends \Civi\Api4\Generic\BasicGetAction { public function getRecords() { + \CRM_Core_Error::deprecatedWarning('APIv4 Entity::getLinks is deprecated.'); $result = []; /** @var \Civi\Api4\Service\Schema\SchemaMap $schema */ $schema = \Civi::container()->get('schema_map'); diff --git a/Civi/Api4/Entity.php b/Civi/Api4/Entity.php index b693895572c2..1660d74d8cb0 100644 --- a/Civi/Api4/Entity.php +++ b/Civi/Api4/Entity.php @@ -128,6 +128,7 @@ public static function getFields($checkPermissions = TRUE) { /** * @param bool $checkPermissions + * @deprecated * @return Action\Entity\GetLinks */ public static function getLinks($checkPermissions = TRUE) { diff --git a/ang/api4Explorer/Explorer.html b/ang/api4Explorer/Explorer.html index f28cec6dbe9b..3d800559426b 100644 --- a/ang/api4Explorer/Explorer.html +++ b/ang/api4Explorer/Explorer.html @@ -10,10 +10,10 @@ <h1 crm-page-title> <div class="panel-heading"> <div class="form-inline"> <span ng-mouseenter="help('entity', paramDoc('$entity'))" ng-mouseleave="help()"> - <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="::{placeholder: ts('Entity'), data: entities}" /> + <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="::{placeholder: ts('Entity'), data: entities, formatResultCssClass: formatResultCssClass}" /> </span> <span ng-mouseenter="help('action', paramDoc('$action'))" ng-mouseleave="help()"> - <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="::{placeholder: ts('Action'), data: actions}" /> + <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="::{placeholder: ts('Action'), data: actions, formatResultCssClass: formatResultCssClass}" /> </span> <input class="form-control api4-index" type="search" ng-model="index" ng-mouseenter="help('index', paramDoc('$index'))" ng-mouseleave="help()" placeholder="{{:: ts('Index') }}" /> <button class="btn btn-success pull-right" crm-icon="fa-bolt" ng-disabled="!entity || !action || loading" ng-click="execute()" ng-mouseenter="help(ts('Execute'), executeDoc())" ng-mouseleave="help()">{{:: ts('Execute') }}</button> diff --git a/ang/api4Explorer/Explorer.js b/ang/api4Explorer/Explorer.js index c51793b3a241..a0cdc38ac82e 100644 --- a/ang/api4Explorer/Explorer.js +++ b/ang/api4Explorer/Explorer.js @@ -2,8 +2,6 @@ // Schema metadata var schema = CRM.vars.api4.schema; - // FK schema data - var links = CRM.vars.api4.links; // Cache list of entities var entities = []; // Cache list of actions @@ -13,7 +11,6 @@ // Api params var params; - angular.module('api4Explorer').config(function($routeProvider) { $routeProvider.when('/explorer/:api4entity?/:api4action?', { controller: 'Api4Explorer', @@ -168,17 +165,17 @@ } }); // Add implicit joins based on schema links - _.each(links[$scope.entity], function(link) { - var linkFields = _.cloneDeep(entityFields(link.entity)), - wildCard = addWildcard ? [{id: link.alias + '.*', text: link.alias + '.*', 'description': 'All core ' + link.entity + ' fields'}] : []; - if (linkFields) { + _.each(entityFields($scope.entity, $scope.action), function(field) { + if (field.fk_entity) { + var linkFields = _.cloneDeep(entityFields(field.fk_entity)), + wildCard = addWildcard ? [{id: field.name + '.*', text: field.name + '.*', 'description': 'All core ' + field.fk_entity + ' fields'}] : []; if (addPseudoconstant) { addPseudoconstants(linkFields, addPseudoconstant); } fieldList.push({ - text: link.alias, - description: 'Implicit join to ' + link.entity, - children: wildCard.concat(formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.')) + text: field.name, + description: 'Implicit join to ' + field.fk_entity, + children: wildCard.concat(formatForSelect2(linkFields, [], 'name', ['description'], field.name + '.')) }); } }); @@ -361,13 +358,13 @@ if (joins[alias]) { return joins[alias].entity; } - // Then lookup implicit links + // Then lookup implicit joins _.each(path, function(node) { - var link = _.find(links[entity], {alias: node}); - if (!link) { + var field = getField(node, entity, $scope.action); + if (!field || !field.fk_entity) { return false; } - entity = link.entity; + entity = field.fk_entity; }); return entity; } @@ -476,10 +473,15 @@ $scope.fieldsAndJoinsAndFunctionsAndWildcards.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'}); }; + // Select2 formatter: Add 'strikethrough' class to deprecated items + $scope.formatResultCssClass = function(result) { + return result.deprecated ? 'strikethrough' : ''; + }; + function selectAction() { $scope.action = $routeParams.api4action; if (!actions.length) { - formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']); + formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params', 'deprecated']); } if ($scope.action) { var actionInfo = _.findWhere(actions, {id: $scope.action}); @@ -958,7 +960,7 @@ if ($scope.entity && $routeParams.api4action !== newVal && !_.isUndefined(newVal)) { $location.url('/explorer/' + $scope.entity + '/' + newVal); } else if (newVal) { - setHelp($scope.entity + '::' + newVal, _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment', 'see'])); + setHelp($scope.entity + '::' + newVal, _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment', 'see', 'deprecated'])); } }); @@ -1413,7 +1415,7 @@ } var linkName = fieldNames.shift(), join = getExplicitJoins()[linkName], - newEntity = join ? join.entity : _.findWhere(links[entity], {alias: linkName}).entity; + newEntity = join ? join.entity : _.findWhere(entityFields(entity, action), {name: linkName}).fk_entity; return get(newEntity, fieldNames); } }