Merge pull request #16098 from MegaphoneJon/event-23-correct
[civicrm-core.git] / Civi / Api4 / Utils / FormattingUtil.php
1 <?php
2
3 /*
4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
6 | |
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 |
10 +--------------------------------------------------------------------+
11 */
12
13 /**
14 *
15 * @package CRM
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 * $Id$
18 *
19 */
20
21
22 namespace Civi\Api4\Utils;
23
24 require_once 'api/v3/utils.php';
25
26 class FormattingUtil {
27
28 /**
29 * Massage values into the format the BAO expects for a write operation
30 *
31 * @param $params
32 * @param $entity
33 * @param $fields
34 * @throws \API_Exception
35 */
36 public static function formatWriteParams(&$params, $entity, $fields) {
37 foreach ($fields as $name => $field) {
38 if (!empty($params[$name])) {
39 $value =& $params[$name];
40 // Hack for null values -- see comment below
41 if ($value === 'null') {
42 $value = 'Null';
43 }
44 self::formatInputValue($value, $field, $entity);
45 // Ensure we have an array for serialized fields
46 if (!empty($field['serialize'] && !is_array($value))) {
47 $value = (array) $value;
48 }
49 }
50 /*
51 * Because of the wacky way that database values are saved we need to format
52 * some of the values here. In this strange world the string 'null' is used to
53 * unset values. Hence if we encounter true null we change it to string 'null'.
54 *
55 * If we encounter the string 'null' then we assume the user actually wants to
56 * set the value to string null. However since the string null is reserved for
57 * unsetting values we must change it. Another quirk of the DB_DataObject is
58 * that it allows 'Null' to be set, but any other variation of string 'null'
59 * will be converted to true null, e.g. 'nuLL', 'NUlL' etc. so we change it to
60 * 'Null'.
61 */
62 elseif (array_key_exists($name, $params) && $params[$name] === NULL) {
63 $params[$name] = 'null';
64 }
65 }
66
67 \CRM_Utils_API_HTMLInputCoder::singleton()->encodeRow($params);
68 }
69
70 /**
71 * Transform raw api input to appropriate format for use in a SQL query.
72 *
73 * This is used by read AND write actions (Get, Create, Update, Replace)
74 *
75 * @param $value
76 * @param $fieldSpec
77 * @param string $entity
78 * Ex: 'Contact', 'Domain'
79 * @throws \API_Exception
80 */
81 public static function formatInputValue(&$value, $fieldSpec, $entity) {
82 if (is_array($value)) {
83 foreach ($value as &$val) {
84 self::formatInputValue($val, $fieldSpec, $entity);
85 }
86 return;
87 }
88 $fk = $fieldSpec['name'] == 'id' ? $entity : $fieldSpec['fk_entity'] ?? NULL;
89
90 if ($fk === 'Domain' && $value === 'current_domain') {
91 $value = \CRM_Core_Config::domainID();
92 }
93
94 if ($fk === 'Contact' && !is_numeric($value)) {
95 $value = \_civicrm_api3_resolve_contactID($value);
96 if ('unknown-user' === $value) {
97 throw new \API_Exception("\"{$fieldSpec['name']}\" \"{$value}\" cannot be resolved to a contact ID", 2002, ['error_field' => $fieldSpec['name'], "type" => "integer"]);
98 }
99 }
100
101 switch ($fieldSpec['data_type'] ?? NULL) {
102 case 'Timestamp':
103 $value = date('Y-m-d H:i:s', strtotime($value));
104 break;
105
106 case 'Date':
107 $value = date('Ymd', strtotime($value));
108 break;
109 }
110
111 $hic = \CRM_Utils_API_HTMLInputCoder::singleton();
112 if (!$hic->isSkippedField($fieldSpec['name'])) {
113 $value = $hic->encodeValue($value);
114 }
115 }
116
117 /**
118 * Unserialize raw DAO values and convert to correct type
119 *
120 * @param array $results
121 * @param array $fields
122 * @param string $entity
123 * @throws \CRM_Core_Exception
124 */
125 public static function formatOutputValues(&$results, $fields, $entity) {
126 foreach ($results as &$result) {
127 // Remove inapplicable contact fields
128 if ($entity === 'Contact' && !empty($result['contact_type'])) {
129 \CRM_Utils_Array::remove($result, self::contactFieldsToRemove($result['contact_type']));
130 }
131 foreach ($result as $field => $value) {
132 $dataType = $fields[$field]['data_type'] ?? ($field == 'id' ? 'Integer' : NULL);
133 if (!empty($fields[$field]['serialize'])) {
134 if (is_string($value)) {
135 $result[$field] = $value = \CRM_Core_DAO::unSerializeField($value, $fields[$field]['serialize']);
136 foreach ($value as $key => $val) {
137 $result[$field][$key] = self::convertDataType($val, $dataType);
138 }
139 }
140 }
141 else {
142 $result[$field] = self::convertDataType($value, $dataType);
143 }
144 }
145 }
146 }
147
148 /**
149 * @param mixed $value
150 * @param string $dataType
151 * @return mixed
152 */
153 public static function convertDataType($value, $dataType) {
154 if (isset($value)) {
155 switch ($dataType) {
156 case 'Boolean':
157 return (bool) $value;
158
159 case 'Integer':
160 return (int) $value;
161
162 case 'Money':
163 case 'Float':
164 return (float) $value;
165 }
166 }
167 return $value;
168 }
169
170 /**
171 * @param string $contactType
172 * @return array
173 */
174 public static function contactFieldsToRemove($contactType) {
175 if (!isset(\Civi::$statics[__CLASS__][__FUNCTION__][$contactType])) {
176 \Civi::$statics[__CLASS__][__FUNCTION__][$contactType] = [];
177 foreach (\CRM_Contact_DAO_Contact::fields() as $field) {
178 if (!empty($field['contactType']) && $field['contactType'] != $contactType) {
179 \Civi::$statics[__CLASS__][__FUNCTION__][$contactType][] = $field['name'];
180 }
181 }
182 }
183 return \Civi::$statics[__CLASS__][__FUNCTION__][$contactType];
184 }
185
186 }