Merge pull request #18449 from colemanw/multiValueAutocomplete
[civicrm-core.git] / tests / phpunit / CRMTraits / Custom / CustomDataTrait.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 use Civi\Api4\CustomGroup;
13 use Civi\Api4\CustomField;
14 use Civi\Api4\OptionValue;
15
16 /**
17 * Trait Custom Data trait.
18 *
19 * Trait for setting up custom data in tests.
20 */
21 trait CRMTraits_Custom_CustomDataTrait {
22
23 /**
24 * Create a custom group with fields of multiple types.
25 *
26 * @param array $groupParams
27 *
28 * @throws \API_Exception
29 * @throws \Civi\API\Exception\UnauthorizedException
30 */
31 public function createCustomGroupWithFieldsOfAllTypes($groupParams = []) {
32 $this->createCustomGroup($groupParams);
33 $this->ids['CustomField'] = $this->createCustomFieldsOfAllTypes();
34 }
35
36 /**
37 * Create a custom group.
38 *
39 * @param array $params
40 *
41 * @return int
42 *
43 * @throws \API_Exception
44 * @throws \Civi\API\Exception\UnauthorizedException
45 */
46 public function createCustomGroup($params = []) {
47 $params = array_merge([
48 'title' => 'Custom Group',
49 'extends' => [$this->entity ?? 'Contact'],
50 'weight' => 5,
51 'style' => 'Inline',
52 'max_multiple' => 0,
53 ], $params);
54 $identifier = $params['name'] ?? $params['title'];
55 $this->ids['CustomGroup'][$identifier] = CustomGroup::create(FALSE)->setValues($params)->execute()->first()['id'];
56 return $this->ids['CustomGroup'][$identifier];
57 }
58
59 /**
60 * Get the table_name for the specified custom group.
61 *
62 * @param string $identifier
63 *
64 * @return string
65 */
66 public function getCustomGroupTable($identifier = 'Custom Group') {
67 return $this->callAPISuccessGetValue('CustomGroup', ['id' => $this->ids['CustomGroup'][$identifier], 'return' => 'table_name']);
68 }
69
70 /**
71 * Get the the column name for the identified custom field.
72 *
73 * @param string $key
74 * Identifier - generally keys map to data type - eg. 'text', 'int' etc.
75 *
76 * @return string
77 */
78 protected function getCustomFieldColumnName($key) {
79 return $this->callAPISuccessGetValue('CustomField', ['id' => $this->getCustomFieldID($key), 'return' => 'column_name']);
80 }
81
82 /**
83 * Create a custom group with a single field.
84 *
85 * @param array $groupParams
86 * Params for the group to be created.
87 * @param string $customFieldType
88 *
89 * @param string|null $identifier
90 *
91 * @param array $fieldParams
92 *
93 * @throws \API_Exception
94 * @throws \CRM_Core_Exception
95 * @throws \Civi\API\Exception\UnauthorizedException
96 */
97 public function createCustomGroupWithFieldOfType($groupParams = [], $customFieldType = 'text', $identifier = NULL, $fieldParams = []) {
98 $supported = ['text', 'select', 'date', 'int', 'contact_reference', 'radio', 'multi_country'];
99 if (!in_array($customFieldType, $supported, TRUE)) {
100 throw new CRM_Core_Exception('we have not yet extracted other custom field types from createCustomFieldsOfAllTypes, Use consistent syntax when you do');
101 }
102 $groupParams['title'] = empty($groupParams['title']) ? $identifier . 'Group with field ' . $customFieldType : $groupParams['title'];
103 $groupParams['name'] = $identifier ?? 'Custom Group';
104 $this->createCustomGroup($groupParams);
105 $reference = &$this->ids['CustomField'][$identifier . $customFieldType];
106 $fieldParams = array_merge($fieldParams, ['custom_group_id' => $this->ids['CustomGroup'][$groupParams['name']]]);
107 switch ($customFieldType) {
108 case 'text':
109 $reference = $this->createTextCustomField($fieldParams)['id'];
110 return;
111
112 case 'select':
113 $reference = $this->createSelectCustomField($fieldParams)['id'];
114 return;
115
116 case 'int':
117 $reference = $this->createIntCustomField($fieldParams)['id'];
118 return;
119
120 case 'date':
121 $reference = $this->createDateCustomField($fieldParams)['id'];
122 return;
123
124 case 'contact_reference':
125 $reference = $this->createContactReferenceCustomField($fieldParams)['id'];
126 return;
127
128 case 'radio':
129 $reference = $this->createIntegerRadioCustomField($fieldParams)['id'];
130 return;
131
132 case 'multi_country':
133 $reference = $this->createMultiCountryCustomField($fieldParams)['id'];
134 return;
135
136 }
137 }
138
139 /**
140 * @return array
141 */
142 public function createCustomFieldsOfAllTypes() {
143 $customGroupID = $this->ids['CustomGroup']['Custom Group'];
144 $ids = [];
145 $ids['text'] = (int) $this->createTextCustomField(['custom_group_id' => $customGroupID])['id'];
146 $ids['select_string'] = (int) $this->createSelectCustomField(['custom_group_id' => $customGroupID])['id'];
147 $ids['select_date'] = (int) $this->createDateCustomField(['custom_group_id' => $customGroupID])['id'];
148 $ids['int'] = (int) $this->createIntCustomField(['custom_group_id' => $customGroupID])['id'];
149 $ids['link'] = (int) $this->createLinkCustomField(['custom_group_id' => $customGroupID])['id'];
150 $ids['file'] = (int) $this->createFileCustomField(['custom_group_id' => $customGroupID])['id'];
151 $ids['country'] = (int) $this->createCountryCustomField(['custom_group_id' => $customGroupID])['id'];
152 $ids['multi_country'] = (int) $this->createMultiCountryCustomField(['custom_group_id' => $customGroupID])['id'];
153 $ids['contact_reference'] = $this->createContactReferenceCustomField(['custom_group_id' => $customGroupID])['id'];
154 $ids['state'] = (int) $this->createStateCustomField(['custom_group_id' => $customGroupID])['id'];
155 $ids['multi_state'] = (int) $this->createMultiStateCustomField(['custom_group_id' => $customGroupID])['id'];
156 $ids['boolean'] = (int) $this->createBooleanCustomField(['custom_group_id' => $customGroupID])['id'];
157 return $ids;
158 }
159
160 /**
161 * Get the custom field name for the relevant key.
162 *
163 * e.g returns 'custom_5' where 5 is the id of the field using the key.
164 *
165 * Generally keys map to data types.
166 *
167 * @param string $key
168 *
169 * @return string
170 */
171 protected function getCustomFieldName($key) {
172 return 'custom_' . $this->getCustomFieldID($key);
173 }
174
175 /**
176 * Add another option to the custom field.
177 *
178 * @param string $key
179 * @param array $values
180 *
181 * @return int
182 * @throws \API_Exception
183 */
184 protected function addOptionToCustomField($key, $values) {
185 $optionGroupID = CustomField::get(FALSE)
186 ->addWhere('id', '=', $this->getCustomFieldID($key))
187 ->addSelect('option_group_id')
188 ->execute()->first()['option_group_id'];
189 return (int) OptionValue::create(FALSE)
190 ->setValues(array_merge(['option_group_id' => $optionGroupID], $values))
191 ->execute()->first()['value'];
192 }
193
194 /**
195 * Get the custom field name for the relevant key.
196 *
197 * e.g returns 'custom_5' where 5 is the id of the field using the key.
198 *
199 * Generally keys map to data types.
200 *
201 * @param string $key
202 *
203 * @return string
204 */
205 protected function getCustomFieldID($key) {
206 return $this->ids['CustomField'][$key];
207 }
208
209 /**
210 * Create a custom text fields.
211 *
212 * @param array $params
213 * Parameter overrides, must include custom_group_id.
214 *
215 * @return array
216 */
217 protected function createIntCustomField($params = []) {
218 $params = array_merge($this->getFieldsValuesByType('Int'), $params);
219 return $this->callAPISuccess('CustomField', 'create', $params)['values'][0];
220 }
221
222 /**
223 * Create a custom text fields.
224 *
225 * @param array $params
226 * Parameter overrides, must include custom_group_id.
227 *
228 * @return array
229 */
230 protected function createBooleanCustomField($params = []) {
231 $params = array_merge($this->getFieldsValuesByType('Boolean'), $params);
232 return $this->callAPISuccess('CustomField', 'create', $params)['values'][0];
233 }
234
235 /**
236 * Create a custom text fields.
237 *
238 * @param array $params
239 * Parameter overrides, must include custom_group_id.
240 *
241 * @return array
242 */
243 protected function createContactReferenceCustomField($params = []) {
244 $params = array_merge($this->getFieldsValuesByType('ContactReference'), $params);
245 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
246 }
247
248 /**
249 * Create a custom text fields.
250 *
251 * @param array $params
252 * Parameter overrides, must include custom_group_id.
253 *
254 * @return array
255 */
256 protected function createTextCustomField($params = []) {
257 $params = array_merge($this->getFieldsValuesByType('String'), $params);
258 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
259 }
260
261 /**
262 * Create a custom text fields.
263 *
264 * @param array $params
265 * Parameter overrides, must include custom_group_id.
266 *
267 * @return array
268 */
269 protected function createLinkCustomField($params = []) {
270 $params = array_merge($this->getFieldsValuesByType('Link'), $params);
271 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
272 }
273
274 /**
275 * Create a custom country fields.
276 *
277 * @param array $params
278 * Parameter overrides, must include custom_group_id.
279 *
280 * @return array
281 */
282 protected function createCountryCustomField($params = []) {
283 $params = array_merge($this->getFieldsValuesByType('Country'), $params);
284 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
285 }
286
287 /**
288 * Create a custom multi select country fields.
289 *
290 * @param array $params
291 * Parameter overrides, must include custom_group_id.
292 *
293 * @return array
294 */
295 protected function createMultiCountryCustomField($params = []) {
296 $params = array_merge($this->getFieldsValuesByType('Country', 'Multi-Select Country'), $params);
297 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
298 }
299
300 /**
301 * Create a custom state fields.
302 *
303 * @param array $params
304 * Parameter overrides, must include custom_group_id.
305 *
306 * @return array
307 */
308 protected function createStateCustomField($params = []) {
309 $params = array_merge($this->getFieldsValuesByType('StateProvince'), $params);
310 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
311 }
312
313 /**
314 * Create a custom multi select state fields.
315 *
316 * @param array $params
317 * Parameter overrides, must include custom_group_id.
318 *
319 * @return array
320 */
321 protected function createMultiStateCustomField($params = []) {
322 $params = array_merge($this->getFieldsValuesByType('StateProvince', 'Multi-Select State/Province'), $params);
323 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
324 }
325
326 /**
327 * Create a custom text fields.
328 *
329 * @param array $params
330 * Parameter overrides, must include custom_group_id.
331 *
332 * @return array
333 */
334 protected function createFileCustomField($params = []) {
335 $params = array_merge($this->getFieldsValuesByType('File'), $params);
336 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
337 }
338
339 /**
340 * Create custom select field.
341 *
342 * @param array $params
343 * Parameter overrides, must include custom_group_id.
344 *
345 * @return array
346 */
347 protected function createSelectCustomField(array $params): array {
348 $params = array_merge($this->getFieldsValuesByType('String', 'Select'), $params);
349 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
350 }
351
352 /**
353 * Create custom select field.
354 *
355 * @param array $params
356 * Parameter overrides, must include custom_group_id.
357 *
358 * @return array
359 */
360 protected function createAutoCompleteCustomField(array $params): array {
361 $params = array_merge($this->getFieldsValuesByType('String', 'Autocomplete-Select'), $params);
362 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
363 }
364
365 /**
366 * Create a custom field of type date.
367 *
368 * @param array $params
369 *
370 * @return array
371 */
372 protected function createDateCustomField($params): array {
373 $params = array_merge($this->getFieldsValuesByType('Date'), $params);
374 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
375 }
376
377 /**
378 * Create a custom field of type radio with integer values.
379 *
380 * @param array $params
381 *
382 * @return array
383 */
384 protected function createIntegerRadioCustomField($params): array {
385 $params = array_merge($this->getFieldsValuesByType('Int', 'Radio'), $params);
386 return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
387 }
388
389 /**
390 * Get default field values for the type of field.
391 *
392 * @param $dataType
393 * @param string $htmlType
394 *
395 * @return mixed
396 */
397 public function getFieldsValuesByType($dataType, $htmlType = 'default') {
398 $values = $this->getAvailableFieldCombinations()[$dataType];
399 return array_merge([
400 'is_searchable' => 1,
401 'sequential' => 1,
402 'default_value' => '',
403 'is_required' => 0,
404 ], array_merge($values['default'], $values[$htmlType])
405 );
406 }
407
408 /**
409 * Get data available for custom fields.
410 *
411 * The 'default' key holds general values. Where more than one html type is an option
412 * then the any values that differ to the defaults are keyed by html key.
413 *
414 * The order below is consistent with the UI.
415 *
416 * @return array
417 */
418 protected function getAvailableFieldCombinations() {
419 return [
420 'String' => [
421 'default' => [
422 'label' => 'Enter text here',
423 'html_type' => 'Text',
424 'data_type' => 'String',
425 'default_value' => 'xyz',
426 'text_length' => 300,
427 ],
428 'Select' => [
429 'label' => 'Pick Color',
430 'html_type' => 'Select',
431 'data_type' => 'String',
432 'text_length' => '',
433 'default_value' => '',
434 'option_values' => [
435 [
436 'label' => 'Red',
437 'value' => 'R',
438 'weight' => 1,
439 'is_active' => 1,
440 ],
441 [
442 'label' => 'Yellow',
443 'value' => 'Y',
444 'weight' => 2,
445 'is_active' => 1,
446 ],
447 [
448 'label' => 'Green',
449 'value' => 'G',
450 'weight' => 3,
451 'is_active' => 1,
452 ],
453 ],
454 ],
455 'Radio' => [
456 'label' => 'Pick Color',
457 'html_type' => 'Radio',
458 'data_type' => 'String',
459 'text_length' => '',
460 'default_value' => '',
461 'option_values' => [
462 [
463 'label' => 'Red',
464 'value' => 'R',
465 'weight' => 1,
466 'is_active' => 1,
467 ],
468 [
469 'label' => 'Yellow',
470 'value' => 'Y',
471 'weight' => 2,
472 'is_active' => 1,
473 ],
474 [
475 'label' => 'Green',
476 'value' => 'G',
477 'weight' => 3,
478 'is_active' => 1,
479 ],
480 ],
481 ],
482 'CheckBox' => [
483 'label' => 'Pick Color',
484 'html_type' => 'CheckBox',
485 'data_type' => 'String',
486 'text_length' => '',
487 'default_value' => '',
488 'option_values' => [
489 [
490 'label' => 'Red',
491 'value' => 'R',
492 'weight' => 1,
493 'is_active' => 1,
494 ],
495 [
496 'label' => 'Yellow',
497 'value' => 'Y',
498 'weight' => 2,
499 'is_active' => 1,
500 ],
501 [
502 'label' => 'Green',
503 'value' => 'G',
504 'weight' => 3,
505 'is_active' => 1,
506 ],
507 ],
508 ],
509 'Multi-Select' => [
510 'label' => 'Pick Color',
511 'html_type' => 'Multi-Select',
512 'data_type' => 'String',
513 'text_length' => '',
514 'default_value' => '',
515 'option_values' => [
516 [
517 'label' => 'Red',
518 'value' => 'R',
519 'weight' => 1,
520 'is_active' => 1,
521 ],
522 [
523 'label' => 'Yellow',
524 'value' => 'Y',
525 'weight' => 2,
526 'is_active' => 1,
527 ],
528 [
529 'label' => 'Green',
530 'value' => 'G',
531 'weight' => 3,
532 'is_active' => 1,
533 ],
534 ],
535 ],
536 'Autocomplete-Select' => [
537 'label' => 'Pick Color',
538 'html_type' => 'Autocomplete-Select',
539 'data_type' => 'String',
540 'text_length' => '',
541 'default_value' => '',
542 'option_values' => [
543 [
544 'label' => 'Red',
545 'value' => 'R',
546 'weight' => 1,
547 'is_active' => 1,
548 ],
549 [
550 'label' => 'Yellow',
551 'value' => 'Y',
552 'weight' => 2,
553 'is_active' => 1,
554 ],
555 [
556 'label' => 'Green',
557 'value' => 'G',
558 'weight' => 3,
559 'is_active' => 1,
560 ],
561 ],
562 ],
563 ],
564 'Int' => [
565 'default' => [
566 'label' => 'Enter integer here',
567 'html_type' => 'Text',
568 'data_type' => 'Int',
569 'default_value' => '4',
570 'is_search_range' => 1,
571 ],
572 'Select' => [
573 'label' => 'Integer select',
574 'html_type' => 'Select',
575 'option_values' => [
576 [
577 'label' => '50',
578 'value' => 3,
579 'weight' => 1,
580 'is_active' => 1,
581 ],
582 [
583 'label' => '100',
584 'value' => 4,
585 'weight' => 2,
586 'is_active' => 1,
587 ],
588 ],
589 ],
590 'Radio' => [
591 'label' => 'Integer radio',
592 'html_type' => 'Radio',
593 'option_values' => [
594 [
595 'label' => '50',
596 'value' => 3,
597 'weight' => 1,
598 'is_active' => 1,
599 ],
600 [
601 'label' => '100',
602 'value' => 4,
603 'weight' => 2,
604 'is_active' => 1,
605 ],
606 [
607 'label' => 'Red Testing',
608 'value' => 5,
609 'weight' => 3,
610 'is_active' => 1,
611 ],
612 ],
613 ],
614 ],
615 'Date' => [
616 'default' => [
617 'name' => 'test_date',
618 'label' => 'Test Date',
619 'html_type' => 'Select Date',
620 'data_type' => 'Date',
621 'default_value' => '20090711',
622 'weight' => 3,
623 'is_search_range' => 1,
624 'time_format' => 1,
625 ],
626 ],
627 'Float' => [
628 'default' => [
629 'label' => 'Number',
630 'html_type' => 'Text',
631 'data_type' => 'Float',
632 ],
633 'Select' => [
634 'label' => 'Number select',
635 'html_type' => 'Select',
636 'option_values' => [
637 [
638 'label' => '50',
639 'value' => 3,
640 'weight' => 1,
641 'is_active' => 1,
642 ],
643 [
644 'label' => '100',
645 'value' => 4,
646 'weight' => 2,
647 'is_active' => 1,
648 ],
649 ],
650 ],
651 'Radio' => [
652 'label' => 'Number radio',
653 'html_type' => 'Radio',
654 'option_values' => [
655 [
656 'label' => '50',
657 'value' => 3,
658 'weight' => 1,
659 'is_active' => 1,
660 ],
661 [
662 'label' => '100',
663 'value' => 4,
664 'weight' => 2,
665 'is_active' => 1,
666 ],
667 ],
668 ],
669 ],
670 'Money' => [
671 'default' => [
672 'label' => 'Money',
673 'html_type' => 'Text',
674 'data_type' => 'Money',
675 ],
676 'Select' => [
677 'label' => 'Money select',
678 'html_type' => 'Select',
679 'option_values' => [
680 [
681 'label' => '50',
682 'value' => 3,
683 'weight' => 1,
684 'is_active' => 1,
685 ],
686 [
687 'label' => '100',
688 'value' => 4,
689 'weight' => 2,
690 'is_active' => 1,
691 ],
692 ],
693 ],
694 'Radio' => [
695 'label' => 'Money radio',
696 'html_type' => 'Radio',
697 'option_values' => [
698 [
699 'label' => '50',
700 'value' => 3,
701 'weight' => 1,
702 'is_active' => 1,
703 ],
704 [
705 'label' => '100',
706 'value' => 4,
707 'weight' => 2,
708 'is_active' => 1,
709 ],
710 ],
711 ],
712 ],
713 'Memo' => [
714 'default' => [
715 'label' => 'Memo',
716 'html_type' => 'TextArea',
717 'data_type' => 'Memo',
718 'attributes' => 'rows=4, cols=60',
719 ],
720 'RichTextEditor' => [
721 'label' => 'Memo Rich Text Editor',
722 'html_type' => 'Memo',
723 ],
724 ],
725 'Boolean' => [
726 'default' => [
727 'data_type' => 'Boolean',
728 'html_type' => 'Radio',
729 'label' => 'Yes No',
730 ],
731 ],
732 'StateProvince' => [
733 'default' => [
734 'data_type' => 'StateProvince',
735 'html_type' => 'Select State/Province',
736 'label' => 'State',
737 'option_type' => 0,
738 ],
739 'Multi-Select State/Province' => [
740 'html_type' => 'Multi-Select State/Province',
741 'label' => 'State-multi',
742 ],
743 ],
744 'Country' => [
745 'default' => [
746 'data_type' => 'Country',
747 'html_type' => 'Select Country',
748 'label' => 'Country',
749 'option_type' => 0,
750 ],
751 'Multi-Select Country' => [
752 'html_type' => 'Multi-Select Country',
753 'label' => 'Country-multi',
754 'option_type' => 0,
755 ],
756 ],
757 'File' => [
758 'default' => [
759 'label' => 'My file',
760 'data_type' => 'File',
761 'html_type' => 'File',
762 ],
763 ],
764 'Link' => [
765 'default' => [
766 'name' => 'test_link',
767 'label' => 'test_link',
768 'html_type' => 'Link',
769 'data_type' => 'Link',
770 'default_value' => 'http://civicrm.org',
771 ],
772 ],
773 'ContactReference' => [
774 'default' => [
775 'label' => 'Contact reference field',
776 'html_type' => 'Autocomplete-Select',
777 'data_type' => 'ContactReference',
778 ],
779 ],
780 ];
781 }
782
783 }