Commit | Line | Data |
---|---|---|
19b53e5b | 1 | <?php |
380f3545 TO |
2 | |
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 | ||
19b53e5b C |
13 | namespace Civi\Api4\Generic\Traits; |
14 | ||
c9e3994d | 15 | use Civi\Api4\CustomField; |
721c9da1 | 16 | use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable; |
19b53e5b | 17 | use Civi\Api4\Utils\FormattingUtil; |
a62d97f3 | 18 | use Civi\Api4\Utils\CoreUtil; |
19b53e5b C |
19 | |
20 | /** | |
21 | * @method string getLanguage() | |
14a9c588 | 22 | * @method $this setLanguage(string $language) |
19b53e5b C |
23 | */ |
24 | trait DAOActionTrait { | |
25 | ||
26 | /** | |
27 | * Specify the language to use if this is a multi-lingual environment. | |
28 | * | |
29 | * E.g. "en_US" or "fr_CA" | |
30 | * | |
31 | * @var string | |
32 | */ | |
33 | protected $language; | |
34 | ||
35 | /** | |
36 | * @return \CRM_Core_DAO|string | |
37 | */ | |
38 | protected function getBaoName() { | |
a62d97f3 | 39 | return CoreUtil::getBAOFromApiName($this->getEntityName()); |
19b53e5b C |
40 | } |
41 | ||
42 | /** | |
a4c7afc0 | 43 | * Convert saved object to array |
19b53e5b | 44 | * |
a4c7afc0 CW |
45 | * Used by create, update & save actions |
46 | * | |
47 | * @param \CRM_Core_DAO $bao | |
48 | * @param array $input | |
19b53e5b C |
49 | * @return array |
50 | */ | |
a4c7afc0 CW |
51 | public function baoToArray($bao, $input) { |
52 | $allFields = array_column($bao->fields(), 'name'); | |
53 | if (!empty($this->reload)) { | |
54 | $inputFields = $allFields; | |
55 | $bao->find(TRUE); | |
56 | } | |
57 | else { | |
58 | $inputFields = array_keys($input); | |
59 | // Convert 'null' input to true null | |
60 | foreach ($input as $key => $val) { | |
61 | if ($val === 'null') { | |
62 | $bao->$key = NULL; | |
63 | } | |
64 | } | |
65 | } | |
19b53e5b | 66 | $values = []; |
a4c7afc0 CW |
67 | foreach ($allFields as $field) { |
68 | if (isset($bao->$field) || in_array($field, $inputFields)) { | |
69 | $values[$field] = $bao->$field ?? NULL; | |
19b53e5b C |
70 | } |
71 | } | |
72 | return $values; | |
73 | } | |
74 | ||
19b53e5b C |
75 | /** |
76 | * Fill field defaults which were declared by the api. | |
77 | * | |
78 | * Note: default values from core are ignored because the BAO or database layer will supply them. | |
79 | * | |
80 | * @param array $params | |
81 | */ | |
82 | protected function fillDefaults(&$params) { | |
83 | $fields = $this->entityFields(); | |
84 | $bao = $this->getBaoName(); | |
85 | $coreFields = array_column($bao::fields(), NULL, 'name'); | |
86 | ||
87 | foreach ($fields as $name => $field) { | |
88 | // If a default value in the api field is different than in core, the api should override it. | |
89 | if (!isset($params[$name]) && !empty($field['default_value']) && $field['default_value'] != \CRM_Utils_Array::pathGet($coreFields, [$name, 'default'])) { | |
90 | $params[$name] = $field['default_value']; | |
91 | } | |
92 | } | |
93 | } | |
94 | ||
95 | /** | |
96 | * Write bao objects as part of a create/update action. | |
97 | * | |
98 | * @param array $items | |
99 | * The records to write to the DB. | |
14a9c588 | 100 | * |
19b53e5b C |
101 | * @return array |
102 | * The records after being written to the DB (e.g. including newly assigned "id"). | |
103 | * @throws \API_Exception | |
14a9c588 | 104 | * @throws \CRM_Core_Exception |
19b53e5b | 105 | */ |
baf63a69 | 106 | protected function writeObjects(&$items) { |
19b53e5b C |
107 | $baoName = $this->getBaoName(); |
108 | ||
bfa91094 CW |
109 | // TODO: Opt-in more entities to use the new writeRecords BAO method. |
110 | $functionNames = [ | |
236f858e | 111 | 'Address' => 'add', |
bfa91094 | 112 | 'CustomField' => 'writeRecords', |
19b53e5b C |
113 | 'EntityTag' => 'add', |
114 | 'GroupContact' => 'add', | |
19b53e5b | 115 | ]; |
bfa91094 CW |
116 | $method = $functionNames[$this->getEntityName()] ?? NULL; |
117 | if (!isset($method)) { | |
118 | $method = method_exists($baoName, 'create') ? 'create' : (method_exists($baoName, 'add') ? 'add' : 'writeRecords'); | |
19b53e5b C |
119 | } |
120 | ||
121 | $result = []; | |
122 | ||
baf63a69 | 123 | foreach ($items as &$item) { |
2929a8fb | 124 | $entityId = $item['id'] ?? NULL; |
37d82abe | 125 | FormattingUtil::formatWriteParams($item, $this->entityFields()); |
19b53e5b | 126 | $this->formatCustomParams($item, $entityId); |
236f858e | 127 | |
bfa91094 CW |
128 | // Skip individual processing if using writeRecords |
129 | if ($method === 'writeRecords') { | |
236f858e CW |
130 | continue; |
131 | } | |
19b53e5b C |
132 | $item['check_permissions'] = $this->getCheckPermissions(); |
133 | ||
134 | // For some reason the contact bao requires this | |
14a9c588 | 135 | if ($entityId && $this->getEntityName() === 'Contact') { |
19b53e5b C |
136 | $item['contact_id'] = $entityId; |
137 | } | |
138 | ||
14a9c588 | 139 | if ($this->getEntityName() === 'Address') { |
1db5a675 | 140 | $createResult = $baoName::$method($item, $this->fixAddress); |
19b53e5b | 141 | } |
19b53e5b | 142 | else { |
236f858e | 143 | $createResult = $baoName::$method($item); |
19b53e5b C |
144 | } |
145 | ||
146 | if (!$createResult) { | |
147 | $errMessage = sprintf('%s write operation failed', $this->getEntityName()); | |
148 | throw new \API_Exception($errMessage); | |
149 | } | |
150 | ||
a4c7afc0 | 151 | $result[] = $this->baoToArray($createResult, $item); |
19b53e5b | 152 | } |
236f858e CW |
153 | |
154 | // Use bulk `writeRecords` method if the BAO doesn't have a create or add method | |
155 | // TODO: reverse this from opt-in to opt-out and default to using `writeRecords` for all BAOs | |
bfa91094 | 156 | if ($method === 'writeRecords') { |
236f858e CW |
157 | $items = array_values($items); |
158 | foreach ($baoName::writeRecords($items) as $i => $createResult) { | |
159 | $result[] = $this->baoToArray($createResult, $items[$i]); | |
160 | } | |
161 | } | |
162 | ||
2929a8fb | 163 | FormattingUtil::formatOutputValues($result, $this->entityFields(), $this->getEntityName()); |
19b53e5b C |
164 | return $result; |
165 | } | |
166 | ||
19b53e5b C |
167 | /** |
168 | * @param array $params | |
169 | * @param int $entityId | |
14a9c588 | 170 | * |
14a9c588 | 171 | * @throws \API_Exception |
172 | * @throws \CRM_Core_Exception | |
19b53e5b C |
173 | */ |
174 | protected function formatCustomParams(&$params, $entityId) { | |
175 | $customParams = []; | |
176 | ||
177 | // $customValueID is the ID of the custom value in the custom table for this | |
178 | // entity (i guess this assumes it's not a multi value entity) | |
179 | foreach ($params as $name => $value) { | |
c9e3994d CW |
180 | $field = $this->getCustomFieldInfo($name); |
181 | if (!$field) { | |
19b53e5b C |
182 | continue; |
183 | } | |
184 | ||
19b53e5b | 185 | // todo are we sure we don't want to allow setting to NULL? need to test |
c9e3994d | 186 | if (NULL !== $value) { |
19b53e5b | 187 | |
c9e3994d | 188 | if ($field['suffix']) { |
721c9da1 | 189 | $options = FormattingUtil::getPseudoconstantList($field, $field['suffix'], $params, $this->getActionName()); |
961e974c CW |
190 | $value = FormattingUtil::replacePseudoconstant($options, $value, TRUE); |
191 | } | |
192 | ||
c9e3994d | 193 | if ($field['html_type'] === 'CheckBox') { |
19b53e5b | 194 | // this function should be part of a class |
c9e3994d | 195 | formatCheckBoxField($value, 'custom_' . $field['id'], $this->getEntityName()); |
19b53e5b C |
196 | } |
197 | ||
a6a158e7 CW |
198 | if ($field['data_type'] === 'ContactReference' && !is_numeric($value)) { |
199 | require_once 'api/v3/utils.php'; | |
200 | $value = \_civicrm_api3_resolve_contactID($value); | |
201 | if ('unknown-user' === $value) { | |
202 | throw new \API_Exception("\"{$field['name']}\" \"{$value}\" cannot be resolved to a contact ID", 2002, ['error_field' => $field['name'], "type" => "integer"]); | |
203 | } | |
204 | } | |
205 | ||
19b53e5b | 206 | \CRM_Core_BAO_CustomField::formatCustomField( |
c9e3994d | 207 | $field['id'], |
19b53e5b C |
208 | $customParams, |
209 | $value, | |
c9e3994d | 210 | $field['custom_group.extends'], |
19b53e5b C |
211 | // todo check when this is needed |
212 | NULL, | |
213 | $entityId, | |
214 | FALSE, | |
215 | FALSE, | |
216 | TRUE | |
217 | ); | |
218 | } | |
219 | } | |
220 | ||
221 | if ($customParams) { | |
222 | $params['custom'] = $customParams; | |
223 | } | |
224 | } | |
225 | ||
c9e3994d CW |
226 | /** |
227 | * Gets field info needed to save custom data | |
228 | * | |
721c9da1 | 229 | * @param string $fieldExpr |
c9e3994d CW |
230 | * Field identifier with possible suffix, e.g. MyCustomGroup.MyField1:label |
231 | * @return array|NULL | |
232 | */ | |
721c9da1 CW |
233 | protected function getCustomFieldInfo(string $fieldExpr) { |
234 | if (strpos($fieldExpr, '.') === FALSE) { | |
c9e3994d CW |
235 | return NULL; |
236 | } | |
721c9da1 | 237 | list($groupName, $fieldName) = explode('.', $fieldExpr); |
c9e3994d | 238 | list($fieldName, $suffix) = array_pad(explode(':', $fieldName), 2, NULL); |
721c9da1 CW |
239 | $cacheKey = "APIv4_Custom_Fields-$groupName"; |
240 | $info = \Civi::cache('metadata')->get($cacheKey); | |
241 | if (!isset($info[$fieldName])) { | |
242 | $info = []; | |
243 | $fields = CustomField::get(FALSE) | |
a6a158e7 | 244 | ->addSelect('id', 'name', 'html_type', 'data_type', 'custom_group.extends') |
c9e3994d CW |
245 | ->addWhere('custom_group.name', '=', $groupName) |
246 | ->execute()->indexBy('name'); | |
721c9da1 CW |
247 | foreach ($fields as $name => $field) { |
248 | $field['custom_field_id'] = $field['id']; | |
249 | $field['name'] = $groupName . '.' . $name; | |
250 | $field['entity'] = CustomGroupJoinable::getEntityFromExtends($field['custom_group.extends']); | |
251 | $info[$name] = $field; | |
252 | } | |
253 | \Civi::cache('metadata')->set($cacheKey, $info); | |
c9e3994d | 254 | } |
721c9da1 | 255 | return isset($info[$fieldName]) ? ['suffix' => $suffix] + $info[$fieldName] : NULL; |
c9e3994d CW |
256 | } |
257 | ||
19b53e5b | 258 | } |