Skip to content

Commit

Permalink
APIv4 - Add EntitySet api for set-ops such as UNION
Browse files Browse the repository at this point in the history
  • Loading branch information
colemanw committed Jun 15, 2023
1 parent 6ba05f8 commit fbdb1ee
Show file tree
Hide file tree
Showing 21 changed files with 1,050 additions and 589 deletions.
62 changes: 62 additions & 0 deletions Civi/Api4/Action/EntitySet/Get.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

namespace Civi\Api4\Action\EntitySet;

use Civi\Api4\Generic\DAOGetAction;
use Civi\Api4\Generic\Result;
use Civi\Api4\Generic\Traits\GroupAndHavingParamTrait;
use Civi\Api4\Generic\Traits\SelectParamTrait;
use Civi\Api4\Query\Api4EntitySetQuery;

/**
* @method array getSets()
* @method setSets(array $sets)
*/
class Get extends \Civi\Api4\Generic\AbstractQueryAction {

use SelectParamTrait;
use GroupAndHavingParamTrait;

/**
* Api queries to combine using UNION DISTINCT or UNION ALL
*
* The SQL rules of unions apply: each query must SELECT the same number of fields
* with matching types (in order). Field names do not have to match; (returned fields
* will use the name from the first query).
*
* @var array
*/
protected $sets = [];

/**
* @param string $type
* 'UNION DISTINCT' or 'UNION ALL'
* @param \Civi\Api4\Generic\DAOGetAction $apiRequest
* @return $this
*/
public function addSet(string $type, DAOGetAction $apiRequest) {
$this->sets[] = [$type, $apiRequest->getEntityName(), $apiRequest->getActionName(), $apiRequest->getParams()];
return $this;
}

/**
* @throws \CRM_Core_Exception
*/
public function _run(Result $result) {
$query = new Api4EntitySetQuery($this);
$rows = $query->run();
\CRM_Utils_API_HTMLInputCoder::singleton()->decodeRows($rows);
$result->exchangeArray($rows);
}

}
53 changes: 53 additions & 0 deletions Civi/Api4/EntitySet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
namespace Civi\Api4;

use Civi\Api4\Generic\BasicGetFieldsAction;

/**
* API to query multiple entities with a UNION.
*
* @searchable none
* @since 5.64
* @package Civi\Api4
*/
class EntitySet extends Generic\AbstractEntity {

/**
* @return \Civi\Api4\Action\EntitySet\Get
*/
public static function get($checkPermissions = TRUE) {
return (new Action\EntitySet\Get('EntitySet', __FUNCTION__))
->setCheckPermissions($checkPermissions);
}

/**
* @return \Civi\Api4\Generic\BasicGetFieldsAction
*/
public static function getFields($checkPermissions = TRUE) {
return (new BasicGetFieldsAction('EntitySet', __FUNCTION__, function() {
return [];
}))->setCheckPermissions($checkPermissions);
}

public static function permissions() {
return [];
}

/**
* @param bool $plural
* @return string
*/
protected static function getEntityTitle($plural = FALSE) {
return $plural ? ts('Entity Sets') : ts('Entity Set');
}

}
4 changes: 2 additions & 2 deletions Civi/Api4/Generic/BasicGetAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ protected function formatRawValues(&$records) {
}
}
}
// Swap raw values with pseudoconstants
FormattingUtil::formatOutputValues($values, $fields, $this->getActionName());
}
// Swap raw values with pseudoconstants
FormattingUtil::formatOutputValues($records, $fields, $this->getActionName());
}

}
59 changes: 1 addition & 58 deletions Civi/Api4/Generic/DAOGetAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@
*
* Perform joins on other related entities using a dot notation.
*
* @method $this setHaving(array $clauses)
* @method array getHaving()
* @method $this setTranslationMode(string|null $mode)
* @method string|null getTranslationMode()
*/
class DAOGetAction extends AbstractGetAction {
use Traits\DAOActionTrait;
use Traits\GroupAndHavingParamTrait;

/**
* Fields to return. Defaults to all standard (non-custom, non-extra) fields `['*']`.
Expand Down Expand Up @@ -66,22 +65,6 @@ class DAOGetAction extends AbstractGetAction {
*/
protected $join = [];

/**
* Field(s) by which to group the results.
*
* @var array
*/
protected $groupBy = [];

/**
* Clause for filtering results after grouping and filters are applied.
*
* Each expression should correspond to an item from the SELECT array.
*
* @var array
*/
protected $having = [];

/**
* Should we automatically overload the result with translated data?
* How do we pick the suitable translation?
Expand Down Expand Up @@ -160,46 +143,6 @@ public function addWhere(string $fieldName, string $op, $value = NULL, bool $isE
return $this;
}

/**
* @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;
}

/**
* @param string $expr
* @param string $op
* @param mixed $value
* @return $this
* @throws \CRM_Core_Exception
*/
public function addHaving(string $expr, string $op, $value = NULL) {
if (!in_array($op, CoreUtil::getOperators())) {
throw new \CRM_Core_Exception('Unsupported operator');
}
$this->having[] = [$expr, $op, $value];
return $this;
}

/**
* @param string $entity
* @param string|bool $type
Expand Down
4 changes: 3 additions & 1 deletion Civi/Api4/Generic/Traits/CustomValueActionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public function isAuthorized(): bool {
*/
protected function writeObjects($items) {
$fields = $this->entityFields();
// Note: Some parts of this loop mutate $item for purposes of internal processing only
// so we do not loop through $items by reference as to preserve the original structure for output.
foreach ($items as $idx => $item) {
FormattingUtil::formatWriteParams($item, $fields);

Expand All @@ -83,8 +85,8 @@ protected function writeObjects($items) {
$tableName = CoreUtil::getTableName($this->getEntityName());
$items[$idx]['id'] = (int) \CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM ' . $tableName);
}
FormattingUtil::formatOutputValues($items[$idx], $fields, 'create');
}
FormattingUtil::formatOutputValues($items, $this->entityFields(), 'create');
return $items;
}

Expand Down
4 changes: 3 additions & 1 deletion Civi/Api4/Generic/Traits/DAOActionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ protected function writeObjects($items) {
}

\CRM_Utils_API_HTMLInputCoder::singleton()->decodeRows($result);
FormattingUtil::formatOutputValues($result, $this->entityFields());
foreach ($result as &$row) {
FormattingUtil::formatOutputValues($row, $this->entityFields());
}
return $result;
}

Expand Down
66 changes: 66 additions & 0 deletions Civi/Api4/Generic/Traits/GroupAndHavingParamTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

namespace Civi\Api4\Generic\Traits;

use Civi\Api4\Utils\CoreUtil;

/**
* @method $this setHaving(array $clauses)
* @method array getHaving()
* @method $this setGroupBy(array $clauses)
* @method array getGroupBy()
* @package Civi\Api4\Generic
*/
trait GroupAndHavingParamTrait {

/**
* Field(s) by which to group the results.
*
* @var array
*/
protected $groupBy = [];

/**
* Clause for filtering results after grouping and filters are applied.
*
* Each expression should correspond to an item from the SELECT array.
*
* @var array
*/
protected $having = [];

/**
* @param string $field
* @return $this
*/
public function addGroupBy(string $field) {
$this->groupBy[] = $field;
return $this;
}

/**
* @param string $expr
* @param string $op
* @param mixed $value
* @return $this
* @throws \CRM_Core_Exception
*/
public function addHaving(string $expr, string $op, $value = NULL) {
if (!in_array($op, CoreUtil::getOperators())) {
throw new \CRM_Core_Exception('Unsupported operator');
}
$this->having[] = [$expr, $op, $value];
return $this;
}

}
Loading

0 comments on commit fbdb1ee

Please sign in to comment.