Merge pull request #15649 from JMAConsulting/core-1346
[civicrm-core.git] / Civi / Api4 / Service / Spec / SpecFormatter.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 * $Id$
18 *
19 */
20
21
19b53e5b
C
22namespace Civi\Api4\Service\Spec;
23
24use CRM_Utils_Array as ArrayHelper;
25use CRM_Core_DAO_AllCoreTables as AllCoreTables;
26
27class SpecFormatter {
28
29 /**
30 * @param FieldSpec[] $fields
31 * @param bool $includeFieldOptions
32 *
33 * @return array
34 */
35 public static function specToArray($fields, $includeFieldOptions = FALSE) {
36 $fieldArray = [];
37
38 foreach ($fields as $field) {
39 if ($includeFieldOptions) {
40 $field->getOptions();
41 }
42 $fieldArray[$field->getName()] = $field->toArray();
43 }
44
45 return $fieldArray;
46 }
47
48 /**
49 * @param array $data
50 * @param string $entity
51 *
52 * @return FieldSpec
53 */
54 public static function arrayToField(array $data, $entity) {
55 $dataTypeName = self::getDataType($data);
56
57 if (!empty($data['custom_group_id'])) {
58 $field = new CustomFieldSpec($data['name'], $entity, $dataTypeName);
59 if (strpos($entity, 'Custom_') !== 0) {
60 $field->setName($data['custom_group.name'] . '.' . $data['name']);
61 }
62 else {
63 $field->setCustomTableName($data['custom_group.table_name']);
64 $field->setCustomFieldColumnName($data['column_name']);
65 }
66 $field->setCustomFieldId(ArrayHelper::value('id', $data));
67 $field->setCustomGroupName($data['custom_group.name']);
68 $field->setTitle(ArrayHelper::value('label', $data));
69 $field->setOptions(self::customFieldHasOptions($data));
70 if (\CRM_Core_BAO_CustomField::isSerialized($data)) {
71 $field->setSerialize(\CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND);
72 }
73 }
74 else {
75 $name = ArrayHelper::value('name', $data);
76 $field = new FieldSpec($name, $entity, $dataTypeName);
77 $field->setRequired((bool) ArrayHelper::value('required', $data, FALSE));
78 $field->setTitle(ArrayHelper::value('title', $data));
79 $field->setOptions(!empty($data['pseudoconstant']));
80 $field->setSerialize(ArrayHelper::value('serialize', $data));
81 }
82
83 $field->setDefaultValue(ArrayHelper::value('default', $data));
84 $field->setDescription(ArrayHelper::value('description', $data));
85 self::setInputTypeAndAttrs($field, $data, $dataTypeName);
86
87 $fkAPIName = ArrayHelper::value('FKApiName', $data);
88 $fkClassName = ArrayHelper::value('FKClassName', $data);
89 if ($fkAPIName || $fkClassName) {
90 $field->setFkEntity($fkAPIName ?: AllCoreTables::getBriefName($fkClassName));
91 }
92
93 return $field;
94 }
95
96 /**
97 * Does this custom field have options
98 *
99 * @param array $field
100 * @return bool
101 */
102 private static function customFieldHasOptions($field) {
103 // This will include boolean fields with Yes/No options.
104 if (in_array($field['html_type'], ['Radio', 'CheckBox'])) {
105 return TRUE;
106 }
107 // Do this before the "Select" string search because date fields have a "Select Date" html_type
108 // and contactRef fields have an "Autocomplete-Select" html_type - contacts are an FK not an option list.
109 if (in_array($field['data_type'], ['ContactReference', 'Date'])) {
110 return FALSE;
111 }
112 if (strpos($field['html_type'], 'Select') !== FALSE) {
113 return TRUE;
114 }
115 return !empty($field['option_group_id']);
116 }
117
118 /**
119 * Get the data type from an array. Defaults to 'data_type' with fallback to
120 * mapping for the integer value 'type'
121 *
122 * @param array $data
123 *
124 * @return string
125 */
126 private static function getDataType(array $data) {
127 if (isset($data['data_type'])) {
128 return !empty($data['time_format']) ? 'Timestamp' : $data['data_type'];
129 }
130
131 $dataTypeInt = ArrayHelper::value('type', $data);
132 $dataTypeName = \CRM_Utils_Type::typeToString($dataTypeInt);
133
134 return $dataTypeName;
135 }
136
137 /**
138 * @param \Civi\Api4\Service\Spec\FieldSpec $fieldSpec
139 * @param array $data
140 * @param string $dataTypeName
141 */
142 public static function setInputTypeAndAttrs(FieldSpec &$fieldSpec, $data, $dataTypeName) {
143 $inputType = isset($data['html']['type']) ? $data['html']['type'] : ArrayHelper::value('html_type', $data);
144 $inputAttrs = ArrayHelper::value('html', $data, []);
145 unset($inputAttrs['type']);
146
147 if (!$inputType) {
148 // If no html type is set, guess
149 switch ($dataTypeName) {
150 case 'Int':
151 $inputType = 'Number';
152 $inputAttrs['min'] = 0;
153 break;
154
155 case 'Text':
156 $inputType = ArrayHelper::value('type', $data) === \CRM_Utils_Type::T_LONGTEXT ? 'TextArea' : 'Text';
157 break;
158
159 case 'Timestamp':
160 $inputType = 'Date';
161 $inputAttrs['time'] = TRUE;
162 break;
163
164 case 'Date':
165 $inputAttrs['time'] = FALSE;
166 break;
167
168 case 'Time':
169 $inputType = 'Date';
170 $inputAttrs['time'] = TRUE;
171 $inputAttrs['date'] = FALSE;
172 break;
173
174 default:
175 $map = [
176 'Email' => 'Email',
177 'Boolean' => 'Checkbox',
178 ];
179 $inputType = ArrayHelper::value($dataTypeName, $map, 'Text');
180 }
181 }
182 if (strstr($inputType, 'Multi-Select') || ($inputType == 'Select' && !empty($data['serialize']))) {
183 $inputAttrs['multiple'] = TRUE;
184 $inputType = 'Select';
185 }
186 $map = [
187 'Select State/Province' => 'Select',
188 'Select Country' => 'Select',
189 'Select Date' => 'Date',
190 'Link' => 'Url',
191 ];
192 $inputType = ArrayHelper::value($inputType, $map, $inputType);
193 if ($inputType == 'Date' && !empty($inputAttrs['formatType'])) {
194 self::setLegacyDateFormat($inputAttrs);
195 }
196 // Date/time settings from custom fields
197 if ($inputType == 'Date' && !empty($data['custom_group_id'])) {
198 $inputAttrs['time'] = empty($data['time_format']) ? FALSE : ($data['time_format'] == 1 ? 12 : 24);
199 $inputAttrs['date'] = $data['date_format'];
200 $inputAttrs['start_date_years'] = (int) $data['start_date_years'];
201 $inputAttrs['end_date_years'] = (int) $data['end_date_years'];
202 }
203 if ($inputType == 'Text' && !empty($data['maxlength'])) {
204 $inputAttrs['maxlength'] = (int) $data['maxlength'];
205 }
206 if ($inputType == 'TextArea') {
207 foreach (['rows', 'cols', 'note_rows', 'note_cols'] as $prop) {
208 if (!empty($data[$prop])) {
209 $inputAttrs[str_replace('note_', '', $prop)] = (int) $data[$prop];
210 }
211 }
212 }
213 $fieldSpec
214 ->setInputType($inputType)
215 ->setInputAttrs($inputAttrs);
216 }
217
218 /**
219 * @param array $inputAttrs
220 */
221 private static function setLegacyDateFormat(&$inputAttrs) {
222 if (empty(\Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']])) {
223 \Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']] = [];
224 $params = ['name' => $inputAttrs['formatType']];
225 \CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_PreferencesDate', $params, \Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']]);
226 }
227 $dateFormat = \Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']];
228 unset($inputAttrs['formatType']);
229 $inputAttrs['time'] = !empty($dateFormat['time_format']);
230 $inputAttrs['date'] = TRUE;
231 $inputAttrs['start_date_years'] = (int) $dateFormat['start'];
232 $inputAttrs['end_date_years'] = (int) $dateFormat['end'];
233 }
234
235}