Use non-deprecated methd
[civicrm-core.git] / api / v3 / Generic / Getlist.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * @package CiviCRM_APIv3
14 */
15
16 /**
17 * Generic api wrapper used for quicksearch and autocomplete.
18 *
19 * @param array $apiRequest
20 *
21 * @return mixed
22 * @throws \CiviCRM_API3_Exception
23 */
24 function civicrm_api3_generic_getList($apiRequest) {
25 $entity = CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($apiRequest['entity']);
26 $request = $apiRequest['params'];
27 $meta = civicrm_api3_generic_getfields(['action' => 'get'] + $apiRequest, FALSE);
28
29 // Hey api, would you like to provide default values?
30 $fnName = "_civicrm_api3_{$entity}_getlist_defaults";
31 $defaults = function_exists($fnName) ? $fnName($request) : [];
32 _civicrm_api3_generic_getList_defaults($entity, $request, $defaults, $meta['values']);
33
34 // Hey api, would you like to format the search params?
35 $fnName = "_civicrm_api3_{$entity}_getlist_params";
36 $fnName = function_exists($fnName) ? $fnName : '_civicrm_api3_generic_getlist_params';
37 $fnName($request);
38
39 $request['params']['check_permissions'] = !empty($apiRequest['params']['check_permissions']);
40 $result = civicrm_api3($entity, 'get', $request['params']);
41 if (!empty($request['input']) && !empty($defaults['search_field_fallback']) && $result['count'] < $request['params']['options']['limit']) {
42 // We support a field fallback. Note we don't do this as an OR query because that could easily
43 // bypass an index & kill the server. We just 'pad' the results if needed with the second
44 // query - this is effectively the same as what the old Ajax::getContactEmail function did.
45 // Since these queries should be quick & often only one should be needed this is a simpler alternative
46 // to constructing a UNION via the api.
47 $request['params'][$defaults['search_field_fallback']] = $request['params'][$defaults['search_field']];
48 if ($request['params']['options']['sort'] === $defaults['search_field']) {
49 // The way indexing works here is that the order by field will be chosen in preference to the
50 // filter field. This can result in really bad performance so use the filter field for the sort.
51 // See https://github.com/civicrm/civicrm-core/pull/16993 for performance test results.
52 $request['params']['options']['sort'] = $defaults['search_field_fallback'];
53 }
54 // Exclude anything returned from the previous query since we are looking for additional rows in this
55 // second query.
56 $request['params'][$defaults['search_field']] = ['NOT LIKE' => $request['params'][$defaults['search_field_fallback']]['LIKE']];
57 $request['params']['options']['limit'] -= $result['count'];
58 $result2 = civicrm_api3($entity, 'get', $request['params']);
59 $result['values'] = array_merge($result['values'], $result2['values']);
60 $result['count'] = count($result['values']);
61 }
62 else {
63 // Re-index to sequential = 0.
64 $result['values'] = array_merge($result['values']);
65 }
66
67 // Hey api, would you like to format the output?
68 $fnName = "_civicrm_api3_{$entity}_getlist_output";
69 $fnName = function_exists($fnName) ? $fnName : '_civicrm_api3_generic_getlist_output';
70 $values = $fnName($result, $request, $entity, $meta['values']);
71
72 _civicrm_api3_generic_getlist_postprocess($result, $request, $values);
73
74 $output = ['page_num' => $request['page_num']];
75
76 // Limit is set for searching but not fetching by id
77 if (!empty($request['params']['options']['limit'])) {
78 // If we have an extra result then this is not the last page
79 $last = $request['params']['options']['limit'] - 1;
80 $output['more_results'] = isset($values[$last]);
81 unset($values[$last]);
82 }
83
84 return civicrm_api3_create_success($values, $request['params'], $entity, 'getlist', CRM_Core_DAO::$_nullObject, $output);
85 }
86
87 /**
88 * Set defaults for api.getlist.
89 *
90 * @param string $entity
91 * @param array $request
92 * @param array $apiDefaults
93 * @param array $fields
94 */
95 function _civicrm_api3_generic_getList_defaults($entity, &$request, $apiDefaults, $fields) {
96 $defaults = [
97 'page_num' => 1,
98 'input' => '',
99 'image_field' => NULL,
100 'color_field' => isset($fields['color']) ? 'color' : NULL,
101 'id_field' => $entity == 'option_value' ? 'value' : 'id',
102 'description_field' => [],
103 'add_wildcard' => Civi::settings()->get('includeWildCardInName'),
104 'params' => [],
105 'extra' => [],
106 ];
107 // Find main field from meta
108 foreach (['sort_name', 'title', 'label', 'name', 'subject'] as $field) {
109 if (isset($fields[$field])) {
110 $defaults['label_field'] = $defaults['search_field'] = $field;
111 break;
112 }
113 }
114 // Find fields to be used for the description
115 foreach (['description'] as $field) {
116 if (isset($fields[$field])) {
117 $defaults['description_field'][] = $field;
118 }
119 }
120 $resultsPerPage = Civi::settings()->get('search_autocomplete_count');
121 if (isset($request['params']) && isset($apiDefaults['params'])) {
122 $request['params'] += $apiDefaults['params'];
123 }
124 $request += $apiDefaults + $defaults;
125 // Default api params
126 $params = [
127 'sequential' => 0,
128 'options' => [],
129 ];
130 // When searching e.g. autocomplete
131 if ($request['input']) {
132 $params[$request['search_field']] = ['LIKE' => ($request['add_wildcard'] ? '%' : '') . $request['input'] . '%'];
133 }
134 // When looking up a field e.g. displaying existing record
135 if (!empty($request['id'])) {
136 if (is_string($request['id']) && strpos($request['id'], ',')) {
137 $request['id'] = explode(',', trim($request['id'], ', '));
138 }
139 // Don't run into search limits when prefilling selection
140 $params['options']['limit'] = NULL;
141 unset($params['options']['offset'], $request['params']['options']['limit'], $request['params']['options']['offset']);
142 $params[$request['id_field']] = is_array($request['id']) ? ['IN' => $request['id']] : $request['id'];
143 }
144 $request['params'] += $params;
145
146 $request['params']['options'] += [
147 // Add pagination parameters
148 'sort' => $request['label_field'],
149 // Adding one extra result allows us to see if there are any more
150 'limit' => $resultsPerPage + 1,
151 // Because sql is zero-based
152 'offset' => ($request['page_num'] - 1) * $resultsPerPage,
153 ];
154 }
155
156 /**
157 * Fallback implementation of getlist_params. May be overridden by individual apis.
158 *
159 * @param array $request
160 */
161 function _civicrm_api3_generic_getlist_params(&$request) {
162 $fieldsToReturn = [$request['id_field'], $request['label_field']];
163 if (!empty($request['image_field'])) {
164 $fieldsToReturn[] = $request['image_field'];
165 }
166 if (!empty($request['color_field'])) {
167 $fieldsToReturn[] = $request['color_field'];
168 }
169 if (!empty($request['description_field'])) {
170 $fieldsToReturn = array_merge($fieldsToReturn, (array) $request['description_field']);
171 }
172 $request['params']['return'] = array_unique(array_merge($fieldsToReturn, $request['extra']));
173 }
174
175 /**
176 * Fallback implementation of getlist_output. May be overridden by individual api functions.
177 *
178 * @param array $result
179 * @param array $request
180 * @param string $entity
181 * @param array $fields
182 *
183 * @return array
184 */
185 function _civicrm_api3_generic_getlist_output($result, $request, $entity, $fields) {
186 $output = [];
187 if (!empty($result['values'])) {
188 foreach ($result['values'] as $row) {
189 $data = [
190 'id' => $row[$request['id_field']],
191 'label' => $row[$request['label_field']],
192 ];
193 if (!empty($request['description_field'])) {
194 $data['description'] = [];
195 foreach ((array) $request['description_field'] as $field) {
196 if (!empty($row[$field])) {
197 if (!isset($fields[$field]['pseudoconstant'])) {
198 $data['description'][] = $row[$field];
199 }
200 else {
201 $data['description'][] = CRM_Core_PseudoConstant::getLabel(
202 _civicrm_api3_get_BAO($entity),
203 $field,
204 $row[$field]
205 );
206 }
207 }
208 }
209 };
210 if (!empty($request['image_field'])) {
211 $data['image'] = $row[$request['image_field']] ?? '';
212 }
213 if (isset($row[$request['color_field']])) {
214 $data['color'] = $row[$request['color_field']];
215 }
216 $output[] = $data;
217 }
218 }
219 return $output;
220 }
221
222 /**
223 * Common postprocess for getlist output
224 *
225 * @param $result
226 * @param $request
227 * @param $values
228 */
229 function _civicrm_api3_generic_getlist_postprocess($result, $request, &$values) {
230 $chains = [];
231 foreach ($request['params'] as $field => $param) {
232 if (substr($field, 0, 4) === 'api.') {
233 $chains[] = $field;
234 }
235 }
236 if (!empty($result['values'])) {
237 foreach (array_values($result['values']) as $num => $row) {
238 foreach ($request['extra'] as $field) {
239 $values[$num]['extra'][$field] = $row[$field] ?? NULL;
240 }
241 foreach ($chains as $chain) {
242 $values[$num][$chain] = $row[$chain] ?? NULL;
243 }
244 }
245 }
246 }
247
248 /**
249 * Provide metadata for this api
250 *
251 * @param array $params
252 * @param array $apiRequest
253 */
254 function _civicrm_api3_generic_getlist_spec(&$params, $apiRequest) {
255 $params += [
256 'page_num' => [
257 'title' => 'Page Number',
258 'description' => "Current page of a multi-page lookup",
259 'type' => CRM_Utils_Type::T_INT,
260 ],
261 'input' => [
262 'title' => 'Search Input',
263 'description' => "String to search on",
264 'type' => CRM_Utils_Type::T_TEXT,
265 ],
266 'params' => [
267 'title' => 'API Params',
268 'description' => "Additional filters to send to the {$apiRequest['entity']} API.",
269 ],
270 'extra' => [
271 'title' => 'Extra',
272 'description' => 'Array of additional fields to return.',
273 ],
274 'image_field' => [
275 'title' => 'Image Field',
276 'description' => "Field that this entity uses to store icons (usually automatic)",
277 'type' => CRM_Utils_Type::T_TEXT,
278 ],
279 'id_field' => [
280 'title' => 'ID Field',
281 'description' => "Field that uniquely identifies this entity (usually automatic)",
282 'type' => CRM_Utils_Type::T_TEXT,
283 ],
284 'description_field' => [
285 'title' => 'Description Field',
286 'description' => "Field that this entity uses to store summary text (usually automatic)",
287 'type' => CRM_Utils_Type::T_TEXT,
288 ],
289 'label_field' => [
290 'title' => 'Label Field',
291 'description' => "Field to display as title of results (usually automatic)",
292 'type' => CRM_Utils_Type::T_TEXT,
293 ],
294 'search_field' => [
295 'title' => 'Search Field',
296 'description' => "Field to search on (assumed to be the same as label field unless otherwise specified)",
297 'type' => CRM_Utils_Type::T_TEXT,
298 ],
299 ];
300 }