Merge pull request #23470 from eileenmcnaughton/import_dataform
[civicrm-core.git] / Civi / Api4 / Generic / DAOGetAction.php
CommitLineData
19b53e5b
C
1<?php
2
380f3545
TO
3/*
4 +--------------------------------------------------------------------+
41498ac5 5 | Copyright CiviCRM LLC. All rights reserved. |
380f3545 6 | |
41498ac5
TO
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 |
380f3545
TO
10 +--------------------------------------------------------------------+
11 */
12
19b53e5b
C
13namespace Civi\Api4\Generic;
14
3c7c8fa6 15use Civi\Api4\Query\Api4SelectQuery;
9d2afe25 16use Civi\Api4\Utils\CoreUtil;
3c7c8fa6 17
19b53e5b 18/**
e3c6d5ff 19 * Retrieve $ENTITIES based on criteria specified in the `where` parameter.
19b53e5b 20 *
fc95d9a5 21 * Use the `select` param to determine which fields are returned, defaults to `[*]`.
19b53e5b
C
22 *
23 * Perform joins on other related entities using a dot notation.
c9e3ae2e
CW
24 *
25 * @method $this setHaving(array $clauses)
26 * @method array getHaving()
19b53e5b
C
27 */
28class DAOGetAction extends AbstractGetAction {
29 use Traits\DAOActionTrait;
30
39e0f675 31 /**
60a62215 32 * Fields to return. Defaults to all standard (non-custom, non-extra) fields `['*']`.
2f69b203 33 *
d836e5b3
AS
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.*']`.
35 *
36 * Multi-record custom field sets are represented as their own entity, so join to that entity to get those custom fields.
39e0f675 37 *
fc95d9a5 38 * Use the dot notation to perform joins in the select clause, e.g. selecting `['*', 'contact.*']` from `Email::get()`
39e0f675
CW
39 * will select all fields for the email + all fields for the related contact.
40 *
41 * @var array
42 * @inheritDoc
43 */
44 protected $select = [];
45
16f5a13d
CW
46 /**
47 * Joins to other entities.
48 *
90908aac
CW
49 * Each join is an array of properties:
50 *
51 * ```
52 * [Entity, Required, Bridge, [field, op, value]...]
53 * ```
54 *
55 * - `Entity`: the name of the api entity to join onto.
56 * - `Required`: `TRUE` for an `INNER JOIN`, `FALSE` for a `LEFT JOIN`.
465bc32a 57 * - `Bridge` (optional): Name of a Bridge to incorporate into the join.
90908aac
CW
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.
61 *
16f5a13d 62 * @var array
465bc32a 63 * @see \Civi\Api4\Generic\Traits\EntityBridge
16f5a13d
CW
64 */
65 protected $join = [];
66
f0acec37
CW
67 /**
68 * Field(s) by which to group the results.
69 *
70 * @var array
71 */
72 protected $groupBy = [];
73
74 /**
75 * Clause for filtering results after grouping and filters are applied.
76 *
77 * Each expression should correspond to an item from the SELECT array.
78 *
79 * @var array
80 */
81 protected $having = [];
82
44a3178f
EM
83 /**
84 * @throws \API_Exception
85 * @throws \CRM_Core_Exception
86 */
19b53e5b 87 public function _run(Result $result) {
06f83d5c
CW
88 // Early return if table doesn't exist yet due to pending upgrade
89 $baoName = $this->getBaoName();
44a3178f
EM
90 if (!$baoName) {
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");
93 }
06f83d5c 94 if (!$baoName::tableHasBeenAdded()) {
3519db92 95 \Civi::log()->warning("Could not read from {$this->getEntityName()} before table has been added. Upgrade required.", ['civi.tag' => 'upgrade_needed']);
06f83d5c
CW
96 return;
97 }
98
19b53e5b 99 $this->setDefaultWhereClause();
39e0f675 100 $this->expandSelectClauseWildcards();
651c4c95 101 $this->getObjects($result);
19b53e5b
C
102 }
103
3c7c8fa6 104 /**
651c4c95 105 * @param \Civi\Api4\Generic\Result $result
3c7c8fa6 106 */
651c4c95
CW
107 protected function getObjects(Result $result) {
108 $getCount = in_array('row_count', $this->getSelect());
109 $onlyCount = $this->getSelect() === ['row_count'];
110
111 if (!$onlyCount) {
54d8b867 112 // Typical case: fetch various fields.
651c4c95
CW
113 $query = new Api4SelectQuery($this);
114 $rows = $query->run();
115 \CRM_Utils_API_HTMLInputCoder::singleton()->decodeRows($rows);
116 $result->exchangeArray($rows);
54d8b867 117
651c4c95
CW
118 // No need to fetch count if we got a result set below the limit
119 if (!$this->getLimit() || count($rows) < $this->getLimit()) {
54d8b867
RLAR
120 if ($getCount) {
121 $result->setCountMatched(count($rows) + $this->getOffset());
122 $getCount = FALSE;
123 }
124 else {
125 // Set rowCount for backward compatibility.
126 $result->rowCount = count($rows) + $this->getOffset();
127 }
651c4c95
CW
128 }
129 }
54d8b867 130
651c4c95
CW
131 if ($getCount) {
132 $query = new Api4SelectQuery($this);
54d8b867 133 $result->setCountMatched($query->getCount());
3c7c8fa6 134 }
3c7c8fa6
CW
135 }
136
1f76bc10
PF
137 /**
138 * @param string $fieldName
139 * @param string $op
140 * @param mixed $value
141 * @param bool $isExpression
142 * @return $this
143 * @throws \API_Exception
144 */
145 public function addWhere(string $fieldName, string $op, $value = NULL, bool $isExpression = FALSE) {
146 if (!in_array($op, CoreUtil::getOperators())) {
147 throw new \API_Exception('Unsupported operator');
148 }
149 $this->where[] = [$fieldName, $op, $value, $isExpression];
150 return $this;
151 }
152
f0acec37
CW
153 /**
154 * @return array
155 */
156 public function getGroupBy(): array {
157 return $this->groupBy;
158 }
159
160 /**
161 * @param array $groupBy
162 * @return $this
163 */
164 public function setGroupBy(array $groupBy) {
165 $this->groupBy = $groupBy;
166 return $this;
167 }
168
169 /**
170 * @param string $field
171 * @return $this
172 */
173 public function addGroupBy(string $field) {
174 $this->groupBy[] = $field;
175 return $this;
176 }
177
178 /**
179 * @param string $expr
180 * @param string $op
181 * @param mixed $value
182 * @return $this
183 * @throws \API_Exception
184 */
185 public function addHaving(string $expr, string $op, $value = NULL) {
9d2afe25 186 if (!in_array($op, CoreUtil::getOperators())) {
f0acec37
CW
187 throw new \API_Exception('Unsupported operator');
188 }
189 $this->having[] = [$expr, $op, $value];
190 return $this;
191 }
192
16f5a13d
CW
193 /**
194 * @param string $entity
266e8deb 195 * @param string|bool $type
90908aac 196 * @param string $bridge
16f5a13d
CW
197 * @param array ...$conditions
198 * @return DAOGetAction
199 */
266e8deb 200 public function addJoin(string $entity, $type = 'LEFT', $bridge = NULL, ...$conditions): DAOGetAction {
90908aac
CW
201 if ($bridge) {
202 array_unshift($conditions, $bridge);
203 }
266e8deb 204 array_unshift($conditions, $entity, $type);
16f5a13d
CW
205 $this->join[] = $conditions;
206 return $this;
207 }
208
209 /**
210 * @param array $join
211 * @return DAOGetAction
212 */
213 public function setJoin(array $join): DAOGetAction {
214 $this->join = $join;
215 return $this;
216 }
217
218 /**
219 * @return array
220 */
221 public function getJoin(): array {
222 return $this->join;
223 }
224
19b53e5b 225}