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