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\Utils\SelectUtil
;
18 * Base class for all `Get` api actions.
20 * @package Civi\Api4\Generic
22 * @method $this setSelect(array $selects) Set array of fields to be selected (wildcard * allowed)
23 * @method array getSelect()
25 abstract class AbstractGetAction
extends AbstractQueryAction
{
28 * Fields to return for each $ENTITY. Defaults to all fields `[*]`.
30 * Use the * wildcard by itself to select all available fields, or use it to match similarly-named fields.
31 * E.g. `is_*` will match fields named is_primary, is_active, etc.
33 * Set to `["row_count"]` to return only the number of $ENTITIES found.
37 protected $select = [];
40 * Only return the number of found items.
44 public function selectRowCount() {
45 $this->select
= ['row_count'];
50 * Adds field defaults to the where clause.
52 * Note: it will skip adding field defaults when fetching records by id,
53 * or if that field has already been added to the where clause.
55 * @throws \API_Exception
57 protected function setDefaultWhereClause() {
58 if (!$this->_itemsToGet('id')) {
59 $fields = $this->entityFields();
60 foreach ($fields as $field) {
61 if (isset($field['default_value']) && !$this->_whereContains($field['name'])) {
62 $this->addWhere($field['name'], '=', $field['default_value']);
69 * Adds all fields matched by the * wildcard
71 * @throws \API_Exception
73 protected function expandSelectClauseWildcards() {
74 $wildFields = array_filter($this->select
, function($item) {
75 return strpos($item, '*') !== FALSE && strpos($item, '.') === FALSE && strpos($item, '(') === FALSE && strpos($item, ' ') === FALSE;
77 foreach ($wildFields as $item) {
78 $pos = array_search($item, array_values($this->select
));
79 $matches = SelectUtil
::getMatchingFields($item, array_column($this->entityFields(), 'name'));
80 array_splice($this->select
, $pos, 1, $matches);
82 $this->select
= array_unique($this->select
);
86 * Helper to parse the WHERE param for getRecords to perform simple pre-filtering.
88 * This is intended to optimize some common use-cases e.g. calling the api to get
89 * one or more records by name or id.
91 * Ex: If getRecords fetches a long list of items each with a unique name,
92 * but the user has specified a single record to retrieve, you can optimize the call
93 * by checking `$this->_itemsToGet('name')` and only fetching the item(s) with that name.
95 * @param string $field
98 protected function _itemsToGet($field) {
99 foreach ($this->where
as $clause) {
100 // Look for exact-match operators (=, IN, or LIKE with no wildcard)
101 if ($clause[0] == $field && (in_array($clause[1], ['=', 'IN'], TRUE) ||
($clause[1] == 'LIKE' && !(is_string($clause[2]) && strpos($clause[2], '%') !== FALSE)))) {
102 return (array) $clause[2];
109 * Helper to see if field(s) should be selected by the getRecords function.
111 * Checks the SELECT, WHERE and ORDER BY params to see what fields are needed.
113 * Note that if no SELECT clause has been set then all fields should be selected
114 * and this function will return TRUE for field expressions that don't contain a :pseudoconstant suffix.
116 * @param string ...$fieldNames
117 * One or more field names to check (uses OR if multiple)
119 * Returns true if any given fields are in use.
121 protected function _isFieldSelected(string ...$fieldNames) {
122 if ((!$this->select
&& strpos($fieldNames[0], ':') === FALSE) ||
array_intersect($fieldNames, array_merge($this->select
, array_keys($this->orderBy
)))) {
125 return $this->_whereContains($fieldNames);
129 * Walk through the where clause and check if field(s) are in use.
131 * @param string|array $fieldName
132 * A single fieldName or an array of names (uses OR if multiple)
133 * @param array $clauses
135 * Returns true if any given fields are found in the where clause.
137 protected function _whereContains($fieldName, $clauses = NULL) {
138 if ($clauses === NULL) {
139 $clauses = $this->where
;
141 $fieldName = (array) $fieldName;
142 foreach ($clauses as $clause) {
143 if (is_array($clause) && is_string($clause[0])) {
144 if (in_array($clause[0], $fieldName)) {
147 elseif (is_array($clause[1])) {
148 return $this->_whereContains($fieldName, $clause[1]);
156 * Add one or more fields to be selected (wildcard * allowed)
157 * @param string ...$fieldNames
160 public function addSelect(string ...$fieldNames) {
161 $this->select
= array_merge($this->select
, $fieldNames);