From: Coleman Watts Date: Sat, 21 Mar 2020 23:48:24 +0000 (-0400) Subject: APIv4 - Add rudimentary support for groupBy X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=fba513f62ec8815e08fa838e0d0501279bf34501;p=civicrm-core.git APIv4 - Add rudimentary support for groupBy --- diff --git a/Civi/Api4/Generic/DAOGetAction.php b/Civi/Api4/Generic/DAOGetAction.php index 100173d962..ae29e3992a 100644 --- a/Civi/Api4/Generic/DAOGetAction.php +++ b/Civi/Api4/Generic/DAOGetAction.php @@ -44,6 +44,13 @@ class DAOGetAction extends AbstractGetAction { */ protected $select = []; + /** + * Field(s) by which to group the results. + * + * @var array + */ + protected $groupBy = []; + public function _run(Result $result) { $this->setDefaultWhereClause(); $this->expandSelectClauseWildcards(); @@ -63,4 +70,29 @@ class DAOGetAction extends AbstractGetAction { return $result; } + /** + * @return array + */ + public function getGroupBy(): array { + return $this->groupBy; + } + + /** + * @param array $groupBy + * @return $this + */ + public function setGroupBy(array $groupBy) { + $this->groupBy = $groupBy; + return $this; + } + + /** + * @param string $field + * @return $this + */ + public function addGroupBy(string $field) { + $this->groupBy[] = $field; + return $this; + } + } diff --git a/Civi/Api4/Query/Api4SelectQuery.php b/Civi/Api4/Query/Api4SelectQuery.php index de08a7b152..4e7082a4dc 100644 --- a/Civi/Api4/Query/Api4SelectQuery.php +++ b/Civi/Api4/Query/Api4SelectQuery.php @@ -60,6 +60,11 @@ class Api4SelectQuery extends SelectQuery { */ public $debugOutput = NULL; + /** + * @var array + */ + public $groupBy = []; + /** * @param \Civi\Api4\Generic\DAOGetAction $apiGet */ @@ -68,6 +73,7 @@ class Api4SelectQuery extends SelectQuery { $this->checkPermissions = $apiGet->getCheckPermissions(); $this->select = $apiGet->getSelect(); $this->where = $apiGet->getWhere(); + $this->groupBy = $apiGet->getGroupBy(); $this->orderBy = $apiGet->getOrderBy(); $this->limit = $apiGet->getLimit(); $this->offset = $apiGet->getOffset(); @@ -100,6 +106,7 @@ class Api4SelectQuery extends SelectQuery { $this->buildWhereClause(); $this->buildOrderBy(); $this->buildLimit(); + $this->buildGroupBy(); return $this->query->toSQL(); } @@ -115,20 +122,21 @@ class Api4SelectQuery extends SelectQuery { $this->debugOutput['sql'][] = $sql; } $query = \CRM_Core_DAO::executeQuery($sql); - + $i = 0; while ($query->fetch()) { + $id = $query->id ?? $i++; if (in_array('row_count', $this->select)) { $results[]['row_count'] = (int) $query->c; break; } - $results[$query->id] = []; + $results[$id] = []; foreach ($this->select as $alias) { $returnName = $alias; if ($this->isOneToOneField($alias)) { $alias = str_replace('.', '_', $alias); - $results[$query->id][$returnName] = property_exists($query, $alias) ? $query->$alias : NULL; + $results[$id][$returnName] = property_exists($query, $alias) ? $query->$alias : NULL; } - }; + } } $event = new PostSelectQueryEvent($results, $this); \Civi::dispatcher()->dispatch(Events::POST_SELECT_QUERY, $event); @@ -145,8 +153,10 @@ class Api4SelectQuery extends SelectQuery { return; } else { - // Always select id field - $this->select = array_merge(['id'], $this->select); + // Always select ID (unless we're doing groupBy). + if (!$this->groupBy) { + $this->select = array_merge(['id'], $this->select); + } // Expand wildcards in joins (the api wrapper already expanded non-joined wildcards) $wildFields = array_filter($this->select, function($item) { @@ -209,6 +219,20 @@ class Api4SelectQuery extends SelectQuery { } } + /** + * + */ + protected function buildGroupBy() { + foreach ($this->groupBy as $field) { + if ($this->isOneToOneField($field) && $this->getField($field)) { + $this->query->groupBy($field['sql_name']); + } + else { + throw new \API_Exception("Invalid field. Cannot group by $field"); + } + } + } + /** * Recursively validate and transform a branch or leaf clause array to SQL. * diff --git a/ang/api4Explorer/Explorer.html b/ang/api4Explorer/Explorer.html index 659917ecb2..ad35e38103 100644 --- a/ang/api4Explorer/Explorer.html +++ b/ang/api4Explorer/Explorer.html @@ -84,20 +84,35 @@
- +
+ groupBy * +
+
+ + +
+
+
+ +
+
orderBy * -
- - +
+
+ + + +
+
chain
diff --git a/ang/api4Explorer/Explorer.js b/ang/api4Explorer/Explorer.js index 09db7d1d02..f75443dcf4 100644 --- a/ang/api4Explorer/Explorer.js +++ b/ang/api4Explorer/Explorer.js @@ -237,7 +237,7 @@ }; $scope.isSpecial = function(name) { - var specialParams = ['select', 'fields', 'action', 'where', 'values', 'defaults', 'orderBy', 'chain']; + var specialParams = ['select', 'fields', 'action', 'where', 'values', 'defaults', 'orderBy', 'chain', 'groupBy']; return _.contains(specialParams, name); }; @@ -374,7 +374,7 @@ deep: format === 'json' }); } - if (typeof objectParams[name] !== 'undefined') { + if (typeof objectParams[name] !== 'undefined' || name === 'groupBy') { $scope.$watch('params.' + name, function(values) { // Remove empty values _.each(values, function(clause, index) { @@ -387,13 +387,17 @@ var field = value; $timeout(function() { if (field) { - var defaultOp = _.cloneDeep(objectParams[name]); - if (name === 'chain') { - var num = $scope.params.chain.length; - defaultOp[0] = field; - field = 'name_me_' + num; + if (name === 'groupBy') { + $scope.params[name].push(field); + } else { + var defaultOp = _.cloneDeep(objectParams[name]); + if (name === 'chain') { + var num = $scope.params.chain.length; + defaultOp[0] = field; + field = 'name_me_' + num; + } + $scope.params[name].push([field, defaultOp]); } - $scope.params[name].push([field, defaultOp]); $scope.controls[name] = null; } });