4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
10 +--------------------------------------------------------------------+
13 namespace Civi\Api4\Generic
;
15 use Civi\Api4\Query\Api4SelectQuery
;
16 use Civi\Api4\Utils\CoreUtil
;
19 * Retrieve $ENTITIES based on criteria specified in the `where` parameter.
21 * Use the `select` param to determine which fields are returned, defaults to `[*]`.
23 * Perform joins on other related entities using a dot notation.
25 * @method $this setHaving(array $clauses)
26 * @method array getHaving()
28 class DAOGetAction
extends AbstractGetAction
{
29 use Traits\DAOActionTrait
;
32 * Fields to return. Defaults to all standard (non-custom, non-extra) fields `['*']`.
34 * The keyword `"custom.*"` selects all custom fields (except those belonging to multi-record custom field sets). So to select all standard + custom fields, select `['*', 'custom.*']`.
36 * Multi-record custom field sets are represented as their own entity, so join to that entity to get those custom fields.
38 * Use the dot notation to perform joins in the select clause, e.g. selecting `['*', 'contact.*']` from `Email::get()`
39 * will select all fields for the email + all fields for the related contact.
44 protected $select = [];
47 * Joins to other entities.
49 * Each join is an array of properties:
52 * [Entity, Required, Bridge, [field, op, value]...]
55 * - `Entity`: the name of the api entity to join onto.
56 * - `Required`: `TRUE` for an `INNER JOIN`, `FALSE` for a `LEFT JOIN`.
57 * - `Bridge` (optional): Name of a Bridge to incorporate into the join.
58 * - `[field, op, value]...`: zero or more conditions for the ON clause, using the same nested format as WHERE and HAVING
59 * but with the difference that "value" is interpreted as an expression (e.g. can be the name of a field).
60 * Enclose literal values with quotes.
63 * @see \Civi\Api4\Generic\Traits\EntityBridge
68 * Field(s) by which to group the results.
72 protected $groupBy = [];
75 * Clause for filtering results after grouping and filters are applied.
77 * Each expression should correspond to an item from the SELECT array.
81 protected $having = [];
84 * @throws \API_Exception
85 * @throws \CRM_Core_Exception
87 public function _run(Result
$result) {
88 // Early return if table doesn't exist yet due to pending upgrade
89 $baoName = $this->getBaoName();
91 // In some cases (eg. site spin-up) the code may attempt to call the api before the entity name is registered.
92 throw new \
API_Exception("BAO for {$this->getEntityName()} is not available. This could be a load-order issue");
94 if (!$baoName::tableHasBeenAdded()) {
95 \Civi
::log()->warning("Could not read from {$this->getEntityName()} before table has been added. Upgrade required.", ['civi.tag' => 'upgrade_needed']);
99 $this->setDefaultWhereClause();
100 $this->expandSelectClauseWildcards();
101 $this->getObjects($result);
105 * @param \Civi\Api4\Generic\Result $result
107 protected function getObjects(Result
$result) {
108 $getCount = in_array('row_count', $this->getSelect());
109 $onlyCount = $this->getSelect() === ['row_count'];
112 $query = new Api4SelectQuery($this);
113 $rows = $query->run();
114 \CRM_Utils_API_HTMLInputCoder
::singleton()->decodeRows($rows);
115 $result->exchangeArray($rows);
116 // No need to fetch count if we got a result set below the limit
117 if (!$this->getLimit() ||
count($rows) < $this->getLimit()) {
118 $result->rowCount
= count($rows) +
$this->getOffset();
123 $query = new Api4SelectQuery($this);
124 $result->rowCount
= $query->getCount();
129 * @param string $fieldName
131 * @param mixed $value
132 * @param bool $isExpression
134 * @throws \API_Exception
136 public function addWhere(string $fieldName, string $op, $value = NULL, bool $isExpression = FALSE) {
137 if (!in_array($op, CoreUtil
::getOperators())) {
138 throw new \
API_Exception('Unsupported operator');
140 $this->where
[] = [$fieldName, $op, $value, $isExpression];
147 public function getGroupBy(): array {
148 return $this->groupBy
;
152 * @param array $groupBy
155 public function setGroupBy(array $groupBy) {
156 $this->groupBy
= $groupBy;
161 * @param string $field
164 public function addGroupBy(string $field) {
165 $this->groupBy
[] = $field;
170 * @param string $expr
172 * @param mixed $value
174 * @throws \API_Exception
176 public function addHaving(string $expr, string $op, $value = NULL) {
177 if (!in_array($op, CoreUtil
::getOperators())) {
178 throw new \
API_Exception('Unsupported operator');
180 $this->having
[] = [$expr, $op, $value];
185 * @param string $entity
186 * @param string|bool $type
187 * @param string $bridge
188 * @param array ...$conditions
189 * @return DAOGetAction
191 public function addJoin(string $entity, $type = 'LEFT', $bridge = NULL, ...$conditions): DAOGetAction
{
193 array_unshift($conditions, $bridge);
195 array_unshift($conditions, $entity, $type);
196 $this->join
[] = $conditions;
202 * @return DAOGetAction
204 public function setJoin(array $join): DAOGetAction
{
212 public function getJoin(): array {