MailSettings - Add button+API for testing a connection
[civicrm-core.git] / Civi / Api4 / Service / Spec / FieldSpec.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
13/**
14 *
15 * @package CRM
ca5cec67 16 * @copyright CiviCRM LLC https://civicrm.org/licensing
380f3545
TO
17 */
18
19
19b53e5b
C
20namespace Civi\Api4\Service\Spec;
21
22use Civi\Api4\Utils\CoreUtil;
23
24class FieldSpec {
25 /**
26 * @var mixed
27 */
28 protected $defaultValue;
29
30 /**
31 * @var string
32 */
33 protected $name;
34
b6b6cb2d
CW
35 /**
36 * @var string
37 */
38 protected $label;
39
19b53e5b
C
40 /**
41 * @var string
42 */
43 protected $title;
44
45 /**
46 * @var string
47 */
48 protected $entity;
49
50 /**
51 * @var string
52 */
53 protected $description;
54
55 /**
56 * @var bool
57 */
58 protected $required = FALSE;
59
60 /**
61 * @var bool
62 */
63 protected $requiredIf;
64
65 /**
7b51867f 66 * @var array|bool
19b53e5b
C
67 */
68 protected $options;
69
70 /**
71 * @var string
72 */
73 protected $dataType;
74
75 /**
76 * @var string
77 */
78 protected $inputType;
79
80 /**
81 * @var array
82 */
83 protected $inputAttrs = [];
84
85 /**
86 * @var string
87 */
88 protected $fkEntity;
89
90 /**
91 * @var int
92 */
93 protected $serialize;
94
f274627b
CW
95 /**
96 * @var string
97 */
98 protected $helpPre;
99
100 /**
101 * @var string
102 */
103 protected $helpPost;
104
7b51867f
SL
105 /**
106 * @var array
107 */
108 protected $permission;
109
a689294c
CW
110 /**
111 * @var string
112 */
113 protected $columnName;
114
19b53e5b
C
115 /**
116 * Aliases for the valid data types
117 *
118 * @var array
119 */
120 public static $typeAliases = [
121 'Int' => 'Integer',
122 'Link' => 'Url',
123 'Memo' => 'Text',
124 ];
125
126 /**
127 * @param string $name
128 * @param string $entity
129 * @param string $dataType
130 */
131 public function __construct($name, $entity, $dataType = 'String') {
132 $this->entity = $entity;
a689294c 133 $this->name = $this->columnName = $name;
19b53e5b
C
134 $this->setDataType($dataType);
135 }
136
137 /**
138 * @return mixed
139 */
140 public function getDefaultValue() {
141 return $this->defaultValue;
142 }
143
144 /**
145 * @param mixed $defaultValue
146 *
147 * @return $this
148 */
149 public function setDefaultValue($defaultValue) {
150 $this->defaultValue = $defaultValue;
151
152 return $this;
153 }
154
155 /**
156 * @return string
157 */
158 public function getName() {
159 return $this->name;
160 }
161
162 /**
163 * @param string $name
164 *
165 * @return $this
166 */
167 public function setName($name) {
168 $this->name = $name;
169
170 return $this;
171 }
172
b6b6cb2d
CW
173 /**
174 * @return string
175 */
176 public function getLabel() {
177 return $this->label;
178 }
179
180 /**
181 * @param string $label
182 *
183 * @return $this
184 */
185 public function setLabel($label) {
186 $this->label = $label;
187
188 return $this;
189 }
190
19b53e5b
C
191 /**
192 * @return string
193 */
194 public function getTitle() {
195 return $this->title;
196 }
197
198 /**
199 * @param string $title
200 *
201 * @return $this
202 */
203 public function setTitle($title) {
204 $this->title = $title;
205
206 return $this;
207 }
208
209 /**
210 * @return string
211 */
212 public function getEntity() {
213 return $this->entity;
214 }
215
216 /**
217 * @return string
218 */
219 public function getDescription() {
220 return $this->description;
221 }
222
223 /**
224 * @param string $description
225 *
226 * @return $this
227 */
228 public function setDescription($description) {
229 $this->description = $description;
230
231 return $this;
232 }
233
234 /**
235 * @return bool
236 */
237 public function isRequired() {
238 return $this->required;
239 }
240
241 /**
242 * @param bool $required
243 *
244 * @return $this
245 */
246 public function setRequired($required) {
247 $this->required = $required;
248
249 return $this;
250 }
251
252 /**
253 * @return bool
254 */
255 public function getRequiredIf() {
256 return $this->requiredIf;
257 }
258
259 /**
260 * @param bool $requiredIf
261 *
262 * @return $this
263 */
264 public function setRequiredIf($requiredIf) {
265 $this->requiredIf = $requiredIf;
266
267 return $this;
268 }
269
270 /**
271 * @return string
272 */
273 public function getDataType() {
274 return $this->dataType;
275 }
276
277 /**
278 * @param $dataType
279 *
280 * @return $this
281 * @throws \Exception
282 */
283 public function setDataType($dataType) {
284 if (array_key_exists($dataType, self::$typeAliases)) {
285 $dataType = self::$typeAliases[$dataType];
286 }
287
288 if (!in_array($dataType, $this->getValidDataTypes())) {
289 throw new \Exception(sprintf('Invalid data type "%s', $dataType));
290 }
291
292 $this->dataType = $dataType;
293
294 return $this;
295 }
296
297 /**
298 * @return int
299 */
300 public function getSerialize() {
301 return $this->serialize;
302 }
303
304 /**
305 * @param int|null $serialize
306 * @return $this
307 */
308 public function setSerialize($serialize) {
309 $this->serialize = $serialize;
310
311 return $this;
312 }
313
7b51867f
SL
314 /**
315 * @param array $permission
316 * @return $this
317 */
318 public function setPermission($permission) {
319 $this->permission = $permission;
320 return $this;
321 }
322
323 /**
324 * @return array
325 */
326 public function getPermission() {
327 return $this->permission;
328 }
329
19b53e5b
C
330 /**
331 * @return string
332 */
333 public function getInputType() {
334 return $this->inputType;
335 }
336
337 /**
338 * @param string $inputType
339 * @return $this
340 */
341 public function setInputType($inputType) {
342 $this->inputType = $inputType;
343
344 return $this;
345 }
346
347 /**
348 * @return array
349 */
350 public function getInputAttrs() {
351 return $this->inputAttrs;
352 }
353
354 /**
355 * @param array $inputAttrs
356 * @return $this
357 */
358 public function setInputAttrs($inputAttrs) {
359 $this->inputAttrs = $inputAttrs;
360
361 return $this;
362 }
363
f274627b
CW
364 /**
365 * @return string|NULL
366 */
367 public function getHelpPre() {
368 return $this->helpPre;
369 }
370
371 /**
372 * @param string|NULL $helpPre
373 */
374 public function setHelpPre($helpPre) {
375 $this->helpPre = is_string($helpPre) && strlen($helpPre) ? $helpPre : NULL;
376 }
377
378 /**
379 * @return string|NULL
380 */
381 public function getHelpPost() {
382 return $this->helpPost;
383 }
384
385 /**
386 * @param string|NULL $helpPost
387 */
388 public function setHelpPost($helpPost) {
389 $this->helpPost = is_string($helpPost) && strlen($helpPost) ? $helpPost : NULL;
390 }
391
19b53e5b
C
392 /**
393 * Add valid types that are not not part of \CRM_Utils_Type::dataTypes
394 *
395 * @return array
396 */
397 private function getValidDataTypes() {
398 $extraTypes = ['Boolean', 'Text', 'Float', 'Url', 'Array'];
399 $extraTypes = array_combine($extraTypes, $extraTypes);
400
401 return array_merge(\CRM_Utils_Type::dataTypes(), $extraTypes);
402 }
403
404 /**
91edcf66 405 * @param array $values
bb6bfd68 406 * @param array|bool $return
19b53e5b
C
407 * @return array
408 */
bb6bfd68 409 public function getOptions($values = [], $return = TRUE) {
19b53e5b
C
410 if (!isset($this->options) || $this->options === TRUE) {
411 $fieldName = $this->getName();
412
413 if ($this instanceof CustomFieldSpec) {
414 // buildOptions relies on the custom_* type of field names
415 $fieldName = sprintf('custom_%d', $this->getCustomFieldId());
416 }
417
bb6bfd68
CW
418 // BAO::buildOptions returns a single-dimensional list, we call that first because of the hook contract,
419 // @see CRM_Utils_Hook::fieldOptions
420 // We then supplement the data with additional properties if requested.
19b53e5b 421 $bao = CoreUtil::getBAOFromApiName($this->getEntity());
bb6bfd68 422 $optionLabels = $bao::buildOptions($fieldName, NULL, $values);
19b53e5b 423
bb6bfd68
CW
424 if (!is_array($optionLabels) || !$optionLabels) {
425 $this->options = FALSE;
426 }
427 else {
428 $this->options = \CRM_Utils_Array::makeNonAssociative($optionLabels, 'id', 'label');
429 if (is_array($return)) {
430 self::addOptionProps($bao, $fieldName, $values, $return);
431 }
19b53e5b 432 }
19b53e5b
C
433 }
434 return $this->options;
435 }
436
bb6bfd68
CW
437 /**
438 * Supplement the data from
439 *
440 * @param \CRM_Core_DAO $baoName
441 * @param string $fieldName
442 * @param array $values
443 * @param array $return
444 */
445 private function addOptionProps($baoName, $fieldName, $values, $return) {
446 // FIXME: For now, call the buildOptions function again and then combine the arrays. Not an ideal approach.
447 // TODO: Teach CRM_Core_Pseudoconstant to always load multidimensional option lists so we can get more properties like 'color' and 'icon',
448 // however that might require a change to the hook_civicrm_fieldOptions signature so that's a bit tricky.
449 if (in_array('name', $return)) {
450 $props['name'] = $baoName::buildOptions($fieldName, 'validate', $values);
451 }
452 $return = array_diff($return, ['id', 'name', 'label']);
453 // CRM_Core_Pseudoconstant doesn't know how to fetch extra stuff like icon, description, color, etc., so we have to invent that wheel here...
454 if ($return) {
455 $optionIds = implode(',', array_column($this->options, 'id'));
456 $optionIndex = array_flip(array_column($this->options, 'id'));
457 if ($this instanceof CustomFieldSpec) {
458 $optionGroupId = \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $this->getCustomFieldId(), 'option_group_id');
459 }
460 else {
461 $dao = new $baoName();
462 $fieldSpec = $dao->getFieldSpec($fieldName);
463 $pseudoconstant = $fieldSpec['pseudoconstant'] ?? NULL;
464 $optionGroupName = $pseudoconstant['optionGroupName'] ?? NULL;
465 $optionGroupId = $optionGroupName ? \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $optionGroupName, 'id', 'name') : NULL;
466 }
467 if (!empty($optionGroupId)) {
468 $extraStuff = \CRM_Core_BAO_OptionValue::getOptionValuesArray($optionGroupId);
469 $keyColumn = $pseudoconstant['keyColumn'] ?? 'value';
470 foreach ($extraStuff as $item) {
471 if (isset($optionIndex[$item[$keyColumn]])) {
472 foreach ($return as $ret) {
473 $this->options[$optionIndex[$item[$keyColumn]]][$ret] = $item[$ret] ?? NULL;
474 }
475 }
476 }
477 }
478 else {
479 // Fetch the abbr if requested using context: abbreviate
480 if (in_array('abbr', $return)) {
481 $props['abbr'] = $baoName::buildOptions($fieldName, 'abbreviate', $values);
482 $return = array_diff($return, ['abbr']);
483 }
484 // Fetch anything else (color, icon, description)
485 if ($return && !empty($pseudoconstant['table']) && \CRM_Utils_Rule::commaSeparatedIntegers($optionIds)) {
486 $sql = "SELECT * FROM {$pseudoconstant['table']} WHERE id IN (%1)";
487 $query = \CRM_Core_DAO::executeQuery($sql, [1 => [$optionIds, 'CommaSeparatedIntegers']]);
488 while ($query->fetch()) {
489 foreach ($return as $ret) {
490 if (property_exists($query, $ret)) {
491 $this->options[$optionIndex[$query->id]][$ret] = $query->$ret;
492 }
493 }
494 }
495 }
496 }
497 }
498 if (isset($props)) {
499 foreach ($this->options as &$option) {
500 foreach ($props as $name => $prop) {
501 $option[$name] = $prop[$option['id']] ?? NULL;
502 }
503 }
504 }
505 }
506
19b53e5b
C
507 /**
508 * @param array|bool $options
509 *
510 * @return $this
511 */
512 public function setOptions($options) {
513 $this->options = $options;
514 return $this;
515 }
516
517 /**
518 * @return string
519 */
520 public function getFkEntity() {
521 return $this->fkEntity;
522 }
523
524 /**
525 * @param string $fkEntity
526 *
527 * @return $this
528 */
529 public function setFkEntity($fkEntity) {
530 $this->fkEntity = $fkEntity;
531
532 return $this;
533 }
534
a689294c
CW
535 /**
536 * @return string
537 */
538 public function getColumnName() {
539 return $this->columnName;
540 }
541
542 /**
543 * @param string $columnName
544 *
545 * @return $this
546 */
547 public function setColumnName($columnName) {
548 $this->columnName = $columnName;
549 return $this;
550 }
551
19b53e5b
C
552 /**
553 * @param array $values
554 * @return array
555 */
556 public function toArray($values = []) {
557 $ret = [];
558 foreach (get_object_vars($this) as $key => $val) {
559 $key = strtolower(preg_replace('/(?=[A-Z])/', '_$0', $key));
560 if (!$values || in_array($key, $values)) {
561 $ret[$key] = $val;
562 }
563 }
564 return $ret;
565 }
566
567}