Add icons to multi-valued custom groups and display on contact tab
[civicrm-core.git] / Civi / Api4 / Query / Api4SelectQuery.php
CommitLineData
19b53e5b
C
1<?php
2/*
3 +--------------------------------------------------------------------+
41498ac5 4 | Copyright CiviCRM LLC. All rights reserved. |
19b53e5b 5 | |
41498ac5
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
19b53e5b
C
9 +--------------------------------------------------------------------+
10 */
11
12namespace Civi\Api4\Query;
13
14use Civi\API\SelectQuery;
19b53e5b 15use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
19b53e5b
C
16use Civi\Api4\Utils\FormattingUtil;
17use Civi\Api4\Utils\CoreUtil;
39e0f675 18use Civi\Api4\Utils\SelectUtil;
19b53e5b
C
19
20/**
21 * A query `node` may be in one of three formats:
22 *
23 * * leaf: [$fieldName, $operator, $criteria]
24 * * negated: ['NOT', $node]
25 * * branch: ['OR|NOT', [$node, $node, ...]]
26 *
27 * Leaf operators are one of:
28 *
29 * * '=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=",
30 * * "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN',
31 * * 'IS NOT NULL', or 'IS NULL'.
32 */
33class Api4SelectQuery extends SelectQuery {
34
35 /**
36 * @var int
37 */
38 protected $apiVersion = 4;
39
3176b04c
CW
40 /**
41 * @var array
37d82abe 42 * [alias => expr][]
3176b04c
CW
43 */
44 protected $selectAliases = [];
45
b65fa6dc
CW
46 /**
47 * If set to an array, this will start collecting debug info.
48 *
49 * @var null|array
50 */
51 public $debugOutput = NULL;
52
fba513f6
CW
53 /**
54 * @var array
55 */
56 public $groupBy = [];
57
48102254
CW
58 public $forceSelectId = TRUE;
59
c9e3ae2e
CW
60 /**
61 * @var array
62 */
63 public $having = [];
64
19b53e5b 65 /**
3c7c8fa6
CW
66 * @param \Civi\Api4\Generic\DAOGetAction $apiGet
67 */
68 public function __construct($apiGet) {
69 $this->entity = $apiGet->getEntityName();
70 $this->checkPermissions = $apiGet->getCheckPermissions();
71 $this->select = $apiGet->getSelect();
72 $this->where = $apiGet->getWhere();
fba513f6 73 $this->groupBy = $apiGet->getGroupBy();
3c7c8fa6
CW
74 $this->orderBy = $apiGet->getOrderBy();
75 $this->limit = $apiGet->getLimit();
76 $this->offset = $apiGet->getOffset();
c9e3ae2e 77 $this->having = $apiGet->getHaving();
48102254
CW
78 // Always select ID of main table unless grouping is used
79 $this->forceSelectId = !$this->groupBy;
3c7c8fa6
CW
80 if ($apiGet->getDebug()) {
81 $this->debugOutput =& $apiGet->_debugOutput;
82 }
5e327f37
CW
83 foreach ($apiGet->entityFields() as $field) {
84 $this->entityFieldNames[] = $field['name'];
9b06167d 85 $field['sql_name'] = '`' . self::MAIN_TABLE_ALIAS . '`.`' . $field['column_name'] . '`';
5e327f37 86 $this->addSpecField($field['name'], $field);
a689294c 87 }
19b53e5b 88
5e327f37
CW
89 $baoName = CoreUtil::getBAOFromApiName($this->entity);
90 $this->constructQueryObject();
19b53e5b
C
91
92 // Add ACLs first to avoid redundant subclauses
93 $this->query->where($this->getAclClause(self::MAIN_TABLE_ALIAS, $baoName));
16f5a13d
CW
94
95 // Add explicit joins. Other joins implied by dot notation may be added later
96 $this->addExplicitJoins($apiGet->getJoin());
19b53e5b
C
97 }
98
99 /**
4e97c268 100 * Builds final sql statement after all params are set.
19b53e5b 101 *
4e97c268
CW
102 * @return string
103 * @throws \API_Exception
104 * @throws \CRM_Core_Exception
105 * @throws \Civi\API\Exception\UnauthorizedException
19b53e5b 106 */
4e97c268 107 public function getSql() {
a689294c 108 $this->buildSelectClause();
19b53e5b 109 $this->buildWhereClause();
a689294c
CW
110 $this->buildOrderBy();
111 $this->buildLimit();
fba513f6 112 $this->buildGroupBy();
c9e3ae2e 113 $this->buildHavingClause();
4e97c268
CW
114 return $this->query->toSQL();
115 }
19b53e5b 116
4e97c268
CW
117 /**
118 * Why walk when you can
119 *
120 * @return array|int
121 */
122 public function run() {
19b53e5b 123 $results = [];
4e97c268 124 $sql = $this->getSql();
b65fa6dc 125 if (is_array($this->debugOutput)) {
0f7babcc 126 $this->debugOutput['sql'][] = $sql;
b65fa6dc 127 }
19b53e5b 128 $query = \CRM_Core_DAO::executeQuery($sql);
19b53e5b
C
129 while ($query->fetch()) {
130 if (in_array('row_count', $this->select)) {
131 $results[]['row_count'] = (int) $query->c;
132 break;
133 }
334bebdd 134 $result = [];
c9e3ae2e 135 foreach ($this->selectAliases as $alias => $expr) {
19b53e5b 136 $returnName = $alias;
3176b04c 137 $alias = str_replace('.', '_', $alias);
334bebdd 138 $result[$returnName] = property_exists($query, $alias) ? $query->$alias : NULL;
fba513f6 139 }
334bebdd 140 $results[] = $result;
19b53e5b 141 }
334bebdd
CW
142 FormattingUtil::formatOutputValues($results, $this->getApiFieldSpec(), $this->getEntity());
143 return $results;
19b53e5b
C
144 }
145
a689294c 146 protected function buildSelectClause() {
9b06167d 147 // An empty select is the same as *
a689294c
CW
148 if (empty($this->select)) {
149 $this->select = $this->entityFieldNames;
19b53e5b 150 }
a689294c
CW
151 elseif (in_array('row_count', $this->select)) {
152 $this->query->select("COUNT(*) AS `c`");
153 return;
19b53e5b 154 }
a689294c 155 else {
48102254 156 if ($this->forceSelectId) {
fba513f6
CW
157 $this->select = array_merge(['id'], $this->select);
158 }
a689294c
CW
159
160 // Expand wildcards in joins (the api wrapper already expanded non-joined wildcards)
161 $wildFields = array_filter($this->select, function($item) {
3176b04c 162 return strpos($item, '*') !== FALSE && strpos($item, '.') !== FALSE && strpos($item, '(') === FALSE && strpos($item, ' ') === FALSE;
a689294c
CW
163 });
164 foreach ($wildFields as $item) {
165 $pos = array_search($item, array_values($this->select));
334bebdd 166 $this->autoJoinFK($item);
a689294c
CW
167 $matches = SelectUtil::getMatchingFields($item, array_keys($this->apiFieldSpec));
168 array_splice($this->select, $pos, 1, $matches);
169 }
170 $this->select = array_unique($this->select);
171 }
3176b04c
CW
172 foreach ($this->select as $item) {
173 $expr = SqlExpression::convert($item, TRUE);
174 $valid = TRUE;
175 foreach ($expr->getFields() as $fieldName) {
176 $field = $this->getField($fieldName);
177 // Remove expressions with unknown fields without raising an error
178 if (!$field) {
179 $this->select = array_diff($this->select, [$item]);
180 if (is_array($this->debugOutput)) {
181 $this->debugOutput['undefined_fields'][] = $fieldName;
182 }
183 $valid = FALSE;
184 }
19b53e5b 185 }
3176b04c 186 if ($valid) {
c9e3ae2e 187 $alias = $expr->getAlias();
19fde02c
CW
188 if ($alias != $expr->getExpr() && isset($this->apiFieldSpec[$alias])) {
189 throw new \API_Exception('Cannot use existing field name as alias');
190 }
c9e3ae2e 191 $this->selectAliases[$alias] = $expr->getExpr();
3176b04c 192 $this->query->select($expr->render($this->apiFieldSpec) . " AS `$alias`");
9b06167d 193 }
19b53e5b
C
194 }
195 }
196
197 /**
198 * @inheritDoc
199 */
200 protected function buildWhereClause() {
201 foreach ($this->where as $clause) {
c9e3ae2e
CW
202 $this->query->where($this->treeWalkClauses($clause, 'WHERE'));
203 }
204 }
205
206 /**
207 * Build HAVING clause.
208 *
209 * Every expression referenced must also be in the SELECT clause.
210 */
211 protected function buildHavingClause() {
212 foreach ($this->having as $clause) {
213 $this->query->having($this->treeWalkClauses($clause, 'HAVING'));
19b53e5b
C
214 }
215 }
216
217 /**
218 * @inheritDoc
219 */
220 protected function buildOrderBy() {
3176b04c 221 foreach ($this->orderBy as $item => $dir) {
19b53e5b 222 if ($dir !== 'ASC' && $dir !== 'DESC') {
3176b04c
CW
223 throw new \API_Exception("Invalid sort direction. Cannot order by $item $dir");
224 }
a4499ec5
CW
225 $expr = $this->getExpression($item);
226 $column = $expr->render($this->apiFieldSpec);
227
228 // Use FIELD() function to sort on pseudoconstant values
229 $suffix = strstr($item, ':');
230 if ($suffix && $expr->getType() === 'SqlField') {
231 $field = $this->getField($item);
232 $options = FormattingUtil::getPseudoconstantList($field['entity'], $field['name'], substr($suffix, 1));
233 if ($options) {
234 asort($options);
235 $column = "FIELD($column,'" . implode("','", array_keys($options)) . "')";
236 }
237 }
238 $this->query->orderBy("$column $dir");
a689294c
CW
239 }
240 }
241
242 /**
243 * @throws \CRM_Core_Exception
244 */
245 protected function buildLimit() {
246 if (!empty($this->limit) || !empty($this->offset)) {
f8bf8e26
CW
247 // If limit is 0, mysql will actually return 0 results. Instead set to maximum possible.
248 $this->query->limit($this->limit ?: '18446744073709551615', $this->offset);
19b53e5b
C
249 }
250 }
251
fba513f6 252 /**
3176b04c 253 * Adds GROUP BY clause to query
fba513f6
CW
254 */
255 protected function buildGroupBy() {
3176b04c 256 foreach ($this->groupBy as $item) {
a4499ec5 257 $this->query->groupBy($this->getExpression($item)->render($this->apiFieldSpec));
fba513f6
CW
258 }
259 }
260
19b53e5b
C
261 /**
262 * Recursively validate and transform a branch or leaf clause array to SQL.
263 *
264 * @param array $clause
c9e3ae2e 265 * @param string $type
16f5a13d 266 * WHERE|HAVING|ON
19b53e5b
C
267 * @return string SQL where clause
268 *
c9e3ae2e
CW
269 * @throws \API_Exception
270 * @uses composeClause() to generate the SQL etc.
19b53e5b 271 */
c9e3ae2e 272 protected function treeWalkClauses($clause, $type) {
19b53e5b
C
273 switch ($clause[0]) {
274 case 'OR':
275 case 'AND':
276 // handle branches
277 if (count($clause[1]) === 1) {
278 // a single set so AND|OR is immaterial
c9e3ae2e 279 return $this->treeWalkClauses($clause[1][0], $type);
19b53e5b
C
280 }
281 else {
282 $sql_subclauses = [];
283 foreach ($clause[1] as $subclause) {
c9e3ae2e 284 $sql_subclauses[] = $this->treeWalkClauses($subclause, $type);
19b53e5b
C
285 }
286 return '(' . implode("\n" . $clause[0], $sql_subclauses) . ')';
287 }
288
289 case 'NOT':
290 // If we get a group of clauses with no operator, assume AND
291 if (!is_string($clause[1][0])) {
292 $clause[1] = ['AND', $clause[1]];
293 }
c9e3ae2e 294 return 'NOT (' . $this->treeWalkClauses($clause[1], $type) . ')';
19b53e5b
C
295
296 default:
c9e3ae2e 297 return $this->composeClause($clause, $type);
19b53e5b
C
298 }
299 }
300
301 /**
302 * Validate and transform a leaf clause array to SQL.
303 * @param array $clause [$fieldName, $operator, $criteria]
c9e3ae2e 304 * @param string $type
16f5a13d 305 * WHERE|HAVING|ON
19b53e5b
C
306 * @return string SQL
307 * @throws \API_Exception
308 * @throws \Exception
309 */
c9e3ae2e 310 protected function composeClause(array $clause, string $type) {
19b53e5b 311 // Pad array for unary operators
c9e3ae2e 312 list($expr, $operator, $value) = array_pad($clause, 3, NULL);
16f5a13d
CW
313 if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
314 throw new \API_Exception('Illegal operator');
315 }
19b53e5b 316
c9e3ae2e
CW
317 // For WHERE clause, expr must be the name of a field.
318 if ($type === 'WHERE') {
319 $field = $this->getField($expr, TRUE);
37d82abe 320 FormattingUtil::formatInputValue($value, $expr, $field);
c9e3ae2e
CW
321 $fieldAlias = $field['sql_name'];
322 }
323 // For HAVING, expr must be an item in the SELECT clause
16f5a13d 324 elseif ($type === 'HAVING') {
37d82abe 325 // Expr references a fieldName or alias
c9e3ae2e
CW
326 if (isset($this->selectAliases[$expr])) {
327 $fieldAlias = $expr;
37d82abe
CW
328 // Attempt to format if this is a real field
329 if (isset($this->apiFieldSpec[$expr])) {
330 FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$expr]);
331 }
c9e3ae2e 332 }
37d82abe 333 // Expr references a non-field expression like a function; convert to alias
c9e3ae2e
CW
334 elseif (in_array($expr, $this->selectAliases)) {
335 $fieldAlias = array_search($expr, $this->selectAliases);
336 }
37d82abe 337 // If either the having or select field contains a pseudoconstant suffix, match and perform substitution
c9e3ae2e 338 else {
37d82abe
CW
339 list($fieldName) = explode(':', $expr);
340 foreach ($this->selectAliases as $selectAlias => $selectExpr) {
341 list($selectField) = explode(':', $selectAlias);
342 if ($selectAlias === $selectExpr && $fieldName === $selectField && isset($this->apiFieldSpec[$fieldName])) {
343 FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$fieldName]);
344 $fieldAlias = $selectAlias;
345 break;
346 }
347 }
348 }
349 if (!isset($fieldAlias)) {
350 throw new \API_Exception("Invalid expression in HAVING clause: '$expr'. Must use a value from SELECT clause.");
c9e3ae2e 351 }
37d82abe 352 $fieldAlias = '`' . $fieldAlias . '`';
c9e3ae2e 353 }
16f5a13d
CW
354 elseif ($type === 'ON') {
355 $expr = $this->getExpression($expr);
356 $fieldName = count($expr->getFields()) === 1 ? $expr->getFields()[0] : NULL;
357 $fieldAlias = $expr->render($this->apiFieldSpec);
358 if (is_string($value)) {
359 $valExpr = $this->getExpression($value);
360 if ($fieldName && $valExpr->getType() === 'SqlString') {
361 FormattingUtil::formatInputValue($valExpr->expr, $fieldName, $this->apiFieldSpec[$fieldName]);
362 }
363 return sprintf('%s %s %s', $fieldAlias, $operator, $valExpr->render($this->apiFieldSpec));
364 }
365 elseif ($fieldName) {
366 FormattingUtil::formatInputValue($value, $fieldName, $this->apiFieldSpec[$fieldName]);
367 }
368 }
19b53e5b 369
c9e3ae2e 370 $sql_clause = \CRM_Core_DAO::createSQLFilter($fieldAlias, [$operator => $value]);
19b53e5b 371 if ($sql_clause === NULL) {
c9e3ae2e 372 throw new \API_Exception("Invalid value in $type clause for '$expr'");
19b53e5b
C
373 }
374 return $sql_clause;
375 }
376
16f5a13d
CW
377 /**
378 * @param string $expr
379 * @return SqlExpression
380 * @throws \API_Exception
381 */
382 protected function getExpression(string $expr) {
383 $sqlExpr = SqlExpression::convert($expr);
384 foreach ($sqlExpr->getFields() as $fieldName) {
385 $this->getField($fieldName, TRUE);
386 }
387 return $sqlExpr;
388 }
389
19b53e5b
C
390 /**
391 * @inheritDoc
392 */
393 protected function getFields() {
394 return $this->apiFieldSpec;
395 }
396
397 /**
398 * Fetch a field from the getFields list
399 *
961e974c 400 * @param string $expr
a689294c 401 * @param bool $strict
3176b04c 402 * In strict mode, this will throw an exception if the field doesn't exist
19b53e5b
C
403 *
404 * @return string|null
a689294c 405 * @throws \API_Exception
19b53e5b 406 */
961e974c
CW
407 public function getField($expr, $strict = FALSE) {
408 // If the expression contains a pseudoconstant filter like activity_type_id:label,
409 // strip it to look up the base field name, then add the field:filter key to apiFieldSpec
410 $col = strpos($expr, ':');
411 $fieldName = $col ? substr($expr, 0, $col) : $expr;
a689294c
CW
412 // Perform join if field not yet available - this will add it to apiFieldSpec
413 if (!isset($this->apiFieldSpec[$fieldName]) && strpos($fieldName, '.')) {
334bebdd 414 $this->autoJoinFK($fieldName);
a689294c
CW
415 }
416 $field = $this->apiFieldSpec[$fieldName] ?? NULL;
3176b04c 417 if ($strict && !$field) {
a689294c 418 throw new \API_Exception("Invalid field '$fieldName'");
19b53e5b 419 }
961e974c 420 $this->apiFieldSpec[$expr] = $field;
3176b04c 421 return $field;
19b53e5b
C
422 }
423
16f5a13d
CW
424 /**
425 * Join onto other entities as specified by the api call.
426 *
427 * @param $joins
428 * @throws \API_Exception
429 * @throws \Civi\API\Exception\NotImplementedException
430 */
431 private function addExplicitJoins($joins) {
432 foreach ($joins as $join) {
433 // First item in the array is the entity name
434 $entity = array_shift($join);
435 // Which might contain an alias. Split on the keyword "AS"
436 list($entity, $alias) = array_pad(explode(' AS ', $entity), 2, NULL);
437 // Ensure alias is a safe string, and supply default if not given
438 $alias = $alias ? \CRM_Utils_String::munge($alias) : strtolower($entity);
439 // First item in the array is a boolean indicating if the join is required (aka INNER or LEFT).
440 // The rest are join conditions.
441 $side = array_shift($join) ? 'INNER' : 'LEFT';
442 $joinEntityGet = \Civi\API\Request::create($entity, 'get', ['version' => 4, 'checkPermissions' => $this->checkPermissions]);
443 foreach ($joinEntityGet->entityFields() as $field) {
444 $field['sql_name'] = '`' . $alias . '`.`' . $field['column_name'] . '`';
445 $field['is_join'] = TRUE;
446 $this->addSpecField($alias . '.' . $field['name'], $field);
447 }
5e327f37
CW
448 $conditions = $this->getJoinConditions($entity, $alias);
449 foreach (array_filter($join) as $clause) {
16f5a13d
CW
450 $conditions[] = $this->treeWalkClauses($clause, 'ON');
451 }
5e327f37 452 $tableName = CoreUtil::getTableName($entity);
16f5a13d
CW
453 $this->join($side, $tableName, $alias, $conditions);
454 }
455 }
456
457 /**
458 * Supply conditions for an explicit join.
459 *
460 * @param $entity
461 * @param $alias
462 * @return array
463 */
464 private function getJoinConditions($entity, $alias) {
465 $conditions = [];
466 // getAclClause() expects a stack of 1-to-1 join fields to help it dedupe, but this is more flexible,
467 // so unless this is a direct 1-to-1 join with the main entity, we'll just hack it
468 // with a padded empty stack to bypass its deduping.
469 $stack = [NULL, NULL];
470 foreach ($this->apiFieldSpec as $name => $field) {
471 if ($field['entity'] !== $entity && $field['fk_entity'] === $entity) {
5e327f37 472 $conditions[] = $this->treeWalkClauses([$name, '=', "$alias.id"], 'ON');
16f5a13d
CW
473 }
474 elseif (strpos($name, "$alias.") === 0 && substr_count($name, '.') === 1 && $field['fk_entity'] === $this->entity) {
5e327f37
CW
475 $conditions[] = $this->treeWalkClauses([$name, '=', 'id'], 'ON');
476 $stack = ['id'];
16f5a13d
CW
477 }
478 }
479 // Hmm, if we came up with > 1 condition, then it's ambiguous how it should be joined so we won't return anything but the generic ACLs
480 if (count($conditions) > 1) {
5e327f37
CW
481 $stack = [NULL, NULL];
482 $conditions = [];
16f5a13d 483 }
5e327f37
CW
484 $baoName = CoreUtil::getBAOFromApiName($entity);
485 $acls = array_values($this->getAclClause($alias, $baoName, $stack));
16f5a13d
CW
486 return array_merge($acls, $conditions);
487 }
488
19b53e5b 489 /**
334bebdd 490 * Joins a path and adds all fields in the joined entity to apiFieldSpec
a689294c 491 *
19b53e5b
C
492 * @param $key
493 * @throws \API_Exception
a689294c 494 * @throws \Exception
19b53e5b 495 */
334bebdd 496 protected function autoJoinFK($key) {
a689294c 497 if (isset($this->apiFieldSpec[$key])) {
9b06167d 498 return;
19b53e5b
C
499 }
500
a689294c
CW
501 $pathArray = explode('.', $key);
502
19b53e5b
C
503 /** @var \Civi\Api4\Service\Schema\Joiner $joiner */
504 $joiner = \Civi::container()->get('joiner');
a689294c
CW
505 // The last item in the path is the field name. We don't care about that; we'll add all fields from the joined entity.
506 array_pop($pathArray);
19b53e5b
C
507 $pathString = implode('.', $pathArray);
508
334bebdd 509 if (!$joiner->canAutoJoin($this->getFrom(), $pathString)) {
9b06167d 510 return;
19b53e5b
C
511 }
512
513 $joinPath = $joiner->join($this, $pathString);
9b06167d 514
19b53e5b
C
515 $lastLink = array_pop($joinPath);
516
a689294c 517 // Custom field names are already prefixed
9b06167d
CW
518 $isCustom = $lastLink instanceof CustomGroupJoinable;
519 if ($isCustom) {
a689294c 520 array_pop($pathArray);
39e0f675 521 }
a689294c 522 $prefix = $pathArray ? implode('.', $pathArray) . '.' : '';
19b53e5b 523 // Cache field info for retrieval by $this->getField()
a689294c 524 foreach ($lastLink->getEntityFields() as $fieldObject) {
6fe7bdee 525 $fieldArray = $fieldObject->toArray();
a689294c 526 $fieldArray['sql_name'] = '`' . $lastLink->getAlias() . '`.`' . $fieldArray['column_name'] . '`';
9b06167d
CW
527 $fieldArray['is_custom'] = $isCustom;
528 $fieldArray['is_join'] = TRUE;
9b06167d 529 $this->addSpecField($prefix . $fieldArray['name'], $fieldArray);
19b53e5b 530 }
19b53e5b
C
531 }
532
19b53e5b
C
533 /**
534 * @return FALSE|string
535 */
536 public function getFrom() {
5e327f37 537 return CoreUtil::getTableName($this->entity);
19b53e5b
C
538 }
539
540 /**
541 * @return string
542 */
543 public function getEntity() {
544 return $this->entity;
545 }
546
547 /**
548 * @return array
549 */
550 public function getSelect() {
551 return $this->select;
552 }
553
554 /**
555 * @return array
556 */
557 public function getWhere() {
558 return $this->where;
559 }
560
561 /**
562 * @return array
563 */
564 public function getOrderBy() {
565 return $this->orderBy;
566 }
567
568 /**
569 * @return mixed
570 */
571 public function getLimit() {
572 return $this->limit;
573 }
574
575 /**
576 * @return mixed
577 */
578 public function getOffset() {
579 return $this->offset;
580 }
581
582 /**
583 * @return array
584 */
585 public function getSelectFields() {
586 return $this->selectFields;
587 }
588
19b53e5b
C
589 /**
590 * @return \CRM_Utils_SQL_Select
591 */
592 public function getQuery() {
593 return $this->query;
594 }
595
596 /**
597 * @return array
598 */
599 public function getJoins() {
600 return $this->joins;
601 }
602
603 /**
604 * @return array
605 */
606 public function getApiFieldSpec() {
607 return $this->apiFieldSpec;
608 }
609
610 /**
611 * @return array
612 */
613 public function getEntityFieldNames() {
614 return $this->entityFieldNames;
615 }
616
617 /**
618 * @return array
619 */
620 public function getAclFields() {
621 return $this->aclFields;
622 }
623
624 /**
625 * @return bool|string
626 */
627 public function getCheckPermissions() {
628 return $this->checkPermissions;
629 }
630
631 /**
632 * @return int
633 */
634 public function getApiVersion() {
635 return $this->apiVersion;
636 }
637
19b53e5b
C
638 /**
639 * Get table name on basis of entity
640 *
19b53e5b
C
641 * @return void
642 */
5e327f37
CW
643 public function constructQueryObject() {
644 $tableName = CoreUtil::getTableName($this->entity);
645 $this->query = \CRM_Utils_SQL_Select::from($tableName . ' ' . self::MAIN_TABLE_ALIAS);
19b53e5b
C
646 }
647
9b06167d
CW
648 /**
649 * @param $path
650 * @param $field
651 */
652 private function addSpecField($path, $field) {
653 // Only add field to spec if we have permission
654 if ($this->checkPermissions && !empty($field['permission']) && !\CRM_Core_Permission::check($field['permission'])) {
655 $this->apiFieldSpec[$path] = FALSE;
656 return;
657 }
658 $defaults = [];
334bebdd 659 $defaults['is_custom'] = $defaults['is_join'] = FALSE;
9b06167d
CW
660 $field += $defaults;
661 $this->apiFieldSpec[$path] = $field;
662 }
663
19b53e5b 664}