CRM/Core - Cleanup boolean expressions
[civicrm-core.git] / CRM / Core / BAO / CustomValueTable.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 * $Id$
17 *
18 */
19class CRM_Core_BAO_CustomValueTable {
20
b5c2afd0 21 /**
100fef9d 22 * @param array $customParams
46fe0a66 23 * @param string $parentOperation Operation being taken on the parent entity.
24 * If we know the parent entity is doing an insert we can skip the
25 * ON DUPLICATE UPDATE - which improves performance and reduces deadlocks.
26 * - edit
27 * - create
b5c2afd0
EM
28 *
29 * @throws Exception
30 */
46fe0a66 31 public static function create($customParams, $parentOperation = NULL) {
6a488035
TO
32 if (empty($customParams) ||
33 !is_array($customParams)
34 ) {
35 return;
36 }
8ef12e64 37
be2fb01f 38 $paramFieldsExtendContactForEntities = [];
63b7d442 39
6a488035
TO
40 foreach ($customParams as $tableName => $tables) {
41 foreach ($tables as $index => $fields) {
353ffa53
TO
42 $sqlOP = NULL;
43 $hookID = NULL;
44 $hookOP = NULL;
45 $entityID = NULL;
6a488035 46 $isMultiple = FALSE;
be2fb01f
CW
47 $set = [];
48 $params = [];
353ffa53 49 $count = 1;
6a488035
TO
50 foreach ($fields as $field) {
51 if (!$sqlOP) {
353ffa53
TO
52 $entityID = $field['entity_id'];
53 $hookID = $field['custom_group_id'];
6a488035
TO
54 $isMultiple = $field['is_multiple'];
55 if (array_key_exists('id', $field)) {
353ffa53
TO
56 $sqlOP = "UPDATE $tableName ";
57 $where = " WHERE id = %{$count}";
be2fb01f 58 $params[$count] = [$field['id'], 'Integer'];
6a488035
TO
59 $count++;
60 $hookOP = 'edit';
61 }
62 else {
353ffa53
TO
63 $sqlOP = "INSERT INTO $tableName ";
64 $where = NULL;
6a488035
TO
65 $hookOP = 'create';
66 }
67 }
68
69 // fix the value before we store it
70 $value = $field['value'];
71 $type = $field['type'];
72 switch ($type) {
73 case 'StateProvince':
74 $type = 'Integer';
75 if (is_array($value)) {
76 $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $value) . CRM_Core_DAO::VALUE_SEPARATOR;
77 $type = 'String';
78 }
ec28b24d 79 elseif (!is_numeric($value) && !strstr($value, CRM_Core_DAO::VALUE_SEPARATOR)) {
6a488035
TO
80 //fix for multi select state, CRM-3437
81 $mulValues = explode(',', $value);
be2fb01f 82 $validStates = [];
6a488035 83 foreach ($mulValues as $key => $stateVal) {
be2fb01f 84 $states = [];
6a488035
TO
85 $states['state_province'] = trim($stateVal);
86
87 CRM_Utils_Array::lookupValue($states, 'state_province',
88 CRM_Core_PseudoConstant::stateProvince(), TRUE
89 );
a7488080 90 if (empty($states['state_province_id'])) {
6a488035
TO
91 CRM_Utils_Array::lookupValue($states, 'state_province',
92 CRM_Core_PseudoConstant::stateProvinceAbbreviation(), TRUE
93 );
94 }
9c1bc317 95 $validStates[] = $states['state_province_id'] ?? NULL;
6a488035
TO
96 }
97 $value = implode(CRM_Core_DAO::VALUE_SEPARATOR,
98 $validStates
99 );
100 $type = 'String';
101 }
102 elseif (!$value) {
103 // CRM-3415
104 // using type of timestamp allows us to sneak in a null into db
105 // gross but effective hack
106 $value = NULL;
107 $type = 'Timestamp';
108 }
ec28b24d 109 else {
110 $type = 'String';
111 }
6a488035
TO
112 break;
113
114 case 'Country':
115 $type = 'Integer';
116 if (is_array($value)) {
117 $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $value) . CRM_Core_DAO::VALUE_SEPARATOR;
118 $type = 'String';
119 }
ec28b24d 120 elseif (!is_numeric($value) && !strstr($value, CRM_Core_DAO::VALUE_SEPARATOR)) {
6a488035
TO
121 //fix for multi select country, CRM-3437
122 $mulValues = explode(',', $value);
be2fb01f 123 $validCountries = [];
6a488035 124 foreach ($mulValues as $key => $countryVal) {
be2fb01f 125 $countries = [];
6a488035
TO
126 $countries['country'] = trim($countryVal);
127 CRM_Utils_Array::lookupValue($countries, 'country',
128 CRM_Core_PseudoConstant::country(), TRUE
129 );
a7488080 130 if (empty($countries['country_id'])) {
6a488035
TO
131 CRM_Utils_Array::lookupValue($countries, 'country',
132 CRM_Core_PseudoConstant::countryIsoCode(), TRUE
133 );
134 }
9c1bc317 135 $validCountries[] = $countries['country_id'] ?? NULL;
6a488035
TO
136 }
137 $value = implode(CRM_Core_DAO::VALUE_SEPARATOR,
138 $validCountries
139 );
140 $type = 'String';
141 }
142 elseif (!$value) {
143 // CRM-3415
144 // using type of timestamp allows us to sneak in a null into db
145 // gross but effective hack
146 $value = NULL;
147 $type = 'Timestamp';
148 }
ec28b24d 149 else {
150 $type = 'String';
151 }
6a488035
TO
152 break;
153
154 case 'File':
155 if (!$field['file_id']) {
156 CRM_Core_Error::fatal();
157 }
158
159 // need to add/update civicrm_entity_file
160 $entityFileDAO = new CRM_Core_DAO_EntityFile();
161 $entityFileDAO->file_id = $field['file_id'];
162 $entityFileDAO->find(TRUE);
163
164 $entityFileDAO->entity_table = $field['table_name'];
165 $entityFileDAO->entity_id = $field['entity_id'];
166 $entityFileDAO->file_id = $field['file_id'];
167 $entityFileDAO->save();
6a488035
TO
168 $value = $field['file_id'];
169 $type = 'String';
170 break;
171
172 case 'Date':
173 $value = CRM_Utils_Date::isoToMysql($value);
174 break;
175
176 case 'Int':
177 if (is_numeric($value)) {
178 $type = 'Integer';
179 }
180 else {
181 $type = 'Timestamp';
182 }
183 break;
184
185 case 'ContactReference':
186 if ($value == NULL) {
187 $type = 'Timestamp';
188 }
189 else {
190 $type = 'Integer';
191 }
192 break;
193
194 case 'RichTextEditor':
195 $type = 'String';
196 break;
197
198 case 'Boolean':
199 //fix for CRM-3290
200 $value = CRM_Utils_String::strtoboolstr($value);
201 if ($value === FALSE) {
202 $type = 'Timestamp';
203 }
204 break;
205
206 default:
207 break;
208 }
52fb3f74 209 if ($value === 'null') {
fd630ef9 210 // when unsetting a value to null, we don't need to validate the type
211 // https://projectllr.atlassian.net/browse/VGQBMP-20
212 $set[$field['column_name']] = $value;
f931b74c 213 }
214 else {
fd630ef9 215 $set[$field['column_name']] = "%{$count}";
be2fb01f 216 $params[$count] = [$value, $type];
fd630ef9 217 $count++;
218 }
63b7d442 219
9c1bc317 220 $fieldExtends = $field['extends'] ?? NULL;
63b7d442
AS
221 if (
222 CRM_Utils_Array::value('entity_table', $field) == 'civicrm_contact'
223 || $fieldExtends == 'Contact'
224 || $fieldExtends == 'Individual'
225 || $fieldExtends == 'Organization'
226 || $fieldExtends == 'Household'
227 ) {
9c1bc317 228 $paramFieldsExtendContactForEntities[$entityID]['custom_' . CRM_Utils_Array::value('custom_field_id', $field)] = $field['custom_field_id'] ?? NULL;
63b7d442 229 }
6a488035
TO
230 }
231
232 if (!empty($set)) {
be2fb01f 233 $setClause = [];
6a488035
TO
234 foreach ($set as $n => $v) {
235 $setClause[] = "$n = $v";
236 }
237 $setClause = implode(',', $setClause);
238 if (!$where) {
239 // do this only for insert
240 $set['entity_id'] = "%{$count}";
be2fb01f 241 $params[$count] = [$entityID, 'Integer'];
6a488035
TO
242 $count++;
243
65aae70e 244 $fieldNames = implode(',', CRM_Utils_Type::escapeAll(array_keys($set), 'MysqlColumnNameOrAlias'));
6a488035 245 $fieldValues = implode(',', array_values($set));
353ffa53 246 $query = "$sqlOP ( $fieldNames ) VALUES ( $fieldValues )";
6a488035 247 // for multiple values we dont do on duplicate key update
46fe0a66 248 if (!$isMultiple && $parentOperation !== 'create') {
6a488035
TO
249 $query .= " ON DUPLICATE KEY UPDATE $setClause";
250 }
251 }
252 else {
253 $query = "$sqlOP SET $setClause $where";
254 }
255 $dao = CRM_Core_DAO::executeQuery($query, $params);
256
257 CRM_Utils_Hook::custom($hookOP,
258 $hookID,
259 $entityID,
260 $fields
261 );
262 }
263 }
264 }
63b7d442
AS
265
266 if (!empty($paramFieldsExtendContactForEntities)) {
be2fb01f 267 CRM_Contact_BAO_Contact::updateGreetingsOnTokenFieldChange($paramFieldsExtendContactForEntities, ['contact_id' => $entityID]);
63b7d442 268 }
6a488035
TO
269 }
270
271 /**
fe482240 272 * Given a field return the mysql data type associated with it.
6a488035 273 *
6a0b768e 274 * @param string $type
fd31fa4c
EM
275 * @param int $maxLength
276 *
72b3a70c
CW
277 * @return string
278 * the mysql data store placeholder
6a488035
TO
279 */
280 public static function fieldToSQLType($type, $maxLength = 255) {
281 if (!isset($maxLength) ||
282 !is_numeric($maxLength) ||
283 $maxLength <= 0
284 ) {
285 $maxLength = 255;
286 }
287
288 switch ($type) {
289 case 'String':
290 case 'Link':
291 return "varchar($maxLength)";
292
293 case 'Boolean':
294 return 'tinyint';
295
296 case 'Int':
297 return 'int';
2aa397bc 298
6a488035
TO
299 // the below three are FK's, and have constraints added to them
300
301 case 'ContactReference':
302 case 'StateProvince':
303 case 'Country':
304 case 'File':
305 return 'int unsigned';
306
307 case 'Float':
308 return 'double';
309
310 case 'Money':
311 return 'decimal(20,2)';
312
313 case 'Memo':
314 case 'RichTextEditor':
315 return 'text';
316
317 case 'Date':
318 return 'datetime';
319
320 default:
321 CRM_Core_Error::fatal();
322 }
323 }
324
b5c2afd0 325 /**
c490a46a 326 * @param array $params
b5c2afd0 327 * @param $entityTable
100fef9d 328 * @param int $entityID
46fe0a66 329 * @param string $parentOperation Operation being taken on the parent entity.
330 * If we know the parent entity is doing an insert we can skip the
331 * ON DUPLICATE UPDATE - which improves performance and reduces deadlocks.
332 * - edit
333 * - create
b5c2afd0 334 */
46fe0a66 335 public static function store($params, $entityTable, $entityID, $parentOperation = NULL) {
be2fb01f 336 $cvParams = [];
6a488035
TO
337 foreach ($params as $fieldID => $param) {
338 foreach ($param as $index => $customValue) {
be2fb01f 339 $cvParam = [
6a488035
TO
340 'entity_table' => $entityTable,
341 'entity_id' => $entityID,
342 'value' => $customValue['value'],
343 'type' => $customValue['type'],
344 'custom_field_id' => $customValue['custom_field_id'],
345 'custom_group_id' => $customValue['custom_group_id'],
346 'table_name' => $customValue['table_name'],
347 'column_name' => $customValue['column_name'],
6b409353 348 'is_multiple' => $customValue['is_multiple'] ?? NULL,
6a488035 349 'file_id' => $customValue['file_id'],
be2fb01f 350 ];
6a488035 351
fe482240 352 // Fix Date type to be timestamp, since that is how we store in db.
6a488035
TO
353 if ($cvParam['type'] == 'Date') {
354 $cvParam['type'] = 'Timestamp';
355 }
356
a7488080 357 if (!empty($customValue['id'])) {
6a488035
TO
358 $cvParam['id'] = $customValue['id'];
359 }
360 if (!array_key_exists($customValue['table_name'], $cvParams)) {
be2fb01f 361 $cvParams[$customValue['table_name']] = [];
6a488035
TO
362 }
363
364 if (!array_key_exists($index, $cvParams[$customValue['table_name']])) {
be2fb01f 365 $cvParams[$customValue['table_name']][$index] = [];
6a488035
TO
366 }
367
368 $cvParams[$customValue['table_name']][$index][] = $cvParam;
369 }
370 }
371 if (!empty($cvParams)) {
46fe0a66 372 self::create($cvParams, $parentOperation);
6a488035
TO
373 }
374 }
375
b5c2afd0 376 /**
fe482240
EM
377 * Post process function.
378 *
c490a46a 379 * @param array $params
b5c2afd0 380 * @param $entityTable
100fef9d 381 * @param int $entityID
b5c2afd0
EM
382 * @param $customFieldExtends
383 */
5fc3ea24 384 public static function postProcess(&$params, $entityTable, $entityID, $customFieldExtends) {
6a488035 385 $customData = CRM_Core_BAO_CustomField::postProcess($params,
6a488035
TO
386 $entityID,
387 $customFieldExtends
388 );
389
390 if (!empty($customData)) {
391 self::store($customData, $entityTable, $entityID);
392 }
393 }
394
395 /**
396 * Return an array of all custom values associated with an entity.
397 *
6a0b768e
TO
398 * @param int $entityID
399 * Identification number of the entity.
400 * @param string $entityType
401 * Type of entity that the entityID corresponds to, specified.
6a488035
TO
402 * as a string with format "'<EntityName>'". Comma separated
403 * list may be used to specify OR matches. Allowable values
404 * are enumerated types in civicrm_custom_group.extends field.
405 * Optional. Default value assumes entityID references a
406 * contact entity.
6a0b768e
TO
407 * @param array $fieldIDs
408 * Optional list of fieldIDs that we want to retrieve. If this.
6a488035
TO
409 * is set the entityType is ignored
410 *
77b97be7 411 * @param bool $formatMultiRecordField
c693f065 412 * @param array $DTparams - CRM-17810 dataTable params for the multiValued custom fields.
77b97be7 413 *
a6c01b45
CW
414 * @return array
415 * Array of custom values for the entity with key=>value
6a488035
TO
416 * pairs specified as civicrm_custom_field.id => custom value.
417 * Empty array if no custom values found.
6a488035 418 */
c693f065 419 public static function &getEntityValues($entityID, $entityType = NULL, $fieldIDs = NULL, $formatMultiRecordField = FALSE, $DTparams = NULL) {
6a488035
TO
420 if (!$entityID) {
421 // adding this here since an empty contact id could have serious repurcussions
422 // like looping forever
423 CRM_Core_Error::fatal('Please file an issue with the backtrace');
424 return NULL;
425 }
426
be2fb01f 427 $cond = [];
6a488035
TO
428 if ($entityType) {
429 $cond[] = "cg.extends IN ( '$entityType' )";
430 }
431 if ($fieldIDs &&
432 is_array($fieldIDs)
433 ) {
434 $fieldIDList = implode(',', $fieldIDs);
435 $cond[] = "cf.id IN ( $fieldIDList )";
436 }
437 if (empty($cond)) {
438 $cond[] = "cg.extends IN ( 'Contact', 'Individual', 'Household', 'Organization' )";
439 }
440 $cond = implode(' AND ', $cond);
441
e87c8fb7 442 $limit = $orderBy = '';
c693f065 443 if (!empty($DTparams['rowCount']) && $DTparams['rowCount'] > 0) {
d51e02d3 444 $limit = " LIMIT " . CRM_Utils_Type::escape($DTparams['offset'], 'Integer') . ", " . CRM_Utils_Type::escape($DTparams['rowCount'], 'Integer');
c693f065 445 }
c693f065 446 if (!empty($DTparams['sort'])) {
447 $orderBy = ' ORDER BY ' . CRM_Utils_Type::escape($DTparams['sort'], 'String');
448 }
449
fe482240 450 // First find all the fields that extend this type of entity.
6a488035
TO
451 $query = "
452SELECT cg.table_name,
453 cg.id as groupID,
454 cg.is_multiple,
455 cf.column_name,
34f51a07
N
456 cf.id as fieldID,
457 cf.data_type as fieldDataType
6a488035
TO
458FROM civicrm_custom_group cg,
459 civicrm_custom_field cf
460WHERE cf.custom_group_id = cg.id
461AND cg.is_active = 1
462AND cf.is_active = 1
463AND $cond
464";
465 $dao = CRM_Core_DAO::executeQuery($query);
466
be2fb01f 467 $select = $fields = $isMultiple = [];
6a488035
TO
468
469 while ($dao->fetch()) {
470 if (!array_key_exists($dao->table_name, $select)) {
be2fb01f
CW
471 $fields[$dao->table_name] = [];
472 $select[$dao->table_name] = [];
6a488035
TO
473 }
474 $fields[$dao->table_name][] = $dao->fieldID;
475 $select[$dao->table_name][] = "{$dao->column_name} AS custom_{$dao->fieldID}";
63d76404 476 $isMultiple[$dao->table_name] = (bool) $dao->is_multiple;
77b97be7 477 $file[$dao->table_name][$dao->fieldID] = $dao->fieldDataType;
6a488035
TO
478 }
479
be2fb01f 480 $result = $sortedResult = [];
6a488035 481 foreach ($select as $tableName => $clauses) {
1c66bdc7 482 if (!empty($DTparams['sort'])) {
483 $query = CRM_Core_DAO::executeQuery("SELECT id FROM {$tableName} WHERE entity_id = {$entityID}");
484 $count = 1;
485 while ($query->fetch()) {
486 $sortedResult["{$query->id}"] = $count;
487 $count++;
488 }
489 }
490
c693f065 491 $query = "SELECT SQL_CALC_FOUND_ROWS id, " . implode(', ', $clauses) . " FROM $tableName WHERE entity_id = $entityID {$orderBy} {$limit}";
6a488035 492 $dao = CRM_Core_DAO::executeQuery($query);
e87c8fb7 493 if (!empty($DTparams)) {
494 $result['count'] = CRM_Core_DAO::singleValueQuery('SELECT FOUND_ROWS()');
495 }
6a488035
TO
496 while ($dao->fetch()) {
497 foreach ($fields[$tableName] as $fieldID) {
498 $fieldName = "custom_{$fieldID}";
499 if ($isMultiple[$tableName]) {
500 if ($formatMultiRecordField) {
d8f34a6e 501 $result["{$dao->id}"]["{$fieldID}"] = $dao->$fieldName;
fe482240
EM
502 }
503 else {
6a488035
TO
504 $result["{$fieldID}_{$dao->id}"] = $dao->$fieldName;
505 }
506 }
507 else {
d8f34a6e 508 $result[$fieldID] = $dao->$fieldName;
6a488035
TO
509 }
510 }
511 }
512 }
e525d6af 513 if (!empty($sortedResult)) {
514 $result['sortedResult'] = $sortedResult;
1c66bdc7 515 }
6a488035
TO
516 return $result;
517 }
518
519 /**
100fef9d 520 * Take in an array of entityID, custom_XXX => value
6a488035
TO
521 * and set the value in the appropriate table. Should also be able
522 * to set the value to null. Follows api parameter/return conventions
523 *
524 * @array $params
525 *
c490a46a 526 * @param array $params
2a6da8d7
EM
527 *
528 * @throws Exception
6a488035 529 * @return array
6a488035 530 */
00be9182 531 public static function setValues(&$params) {
d1b0ffad
CW
532 // For legacy reasons, accept this param in either format
533 if (empty($params['entityID']) && !empty($params['entity_id'])) {
534 $params['entityID'] = $params['entity_id'];
535 }
6a488035 536
d1b0ffad 537 if (!isset($params['entityID']) || !CRM_Utils_Type::validate($params['entityID'], 'Integer', FALSE)) {
fec42980 538 return CRM_Core_Error::createAPIError(ts('entity_id needs to be set and of type Integer'));
6a488035
TO
539 }
540
541 // first collect all the id/value pairs. The format is:
542 // custom_X => value or custom_X_VALUEID => value (for multiple values), VALUEID == -1, -2 etc for new insertions
be2fb01f 543 $fieldValues = [];
6a488035
TO
544 foreach ($params as $n => $v) {
545 if ($customFieldInfo = CRM_Core_BAO_CustomField::getKeyID($n, TRUE)) {
546 $fieldID = (int ) $customFieldInfo[0];
547 if (CRM_Utils_Type::escape($fieldID, 'Integer', FALSE) === NULL) {
548 return CRM_Core_Error::createAPIError(ts('field ID needs to be of type Integer for index %1',
be2fb01f 549 [1 => $fieldID]
353ffa53 550 ));
6a488035
TO
551 }
552 if (!array_key_exists($fieldID, $fieldValues)) {
be2fb01f 553 $fieldValues[$fieldID] = [];
6a488035
TO
554 }
555 $id = -1;
556 if ($customFieldInfo[1]) {
557 $id = (int ) $customFieldInfo[1];
558 }
be2fb01f 559 $fieldValues[$fieldID][] = [
6a488035
TO
560 'value' => $v,
561 'id' => $id,
be2fb01f 562 ];
6a488035
TO
563 }
564 }
565
566 $fieldIDList = implode(',', array_keys($fieldValues));
567
568 // format it so that we can just use create
569 $sql = "
570SELECT cg.table_name as table_name ,
571 cg.id as cg_id ,
572 cg.is_multiple as is_multiple,
63b7d442 573 cg.extends as extends,
6a488035
TO
574 cf.column_name as column_name,
575 cf.id as cf_id ,
3766bd36 576 cf.html_type as html_type ,
6a488035
TO
577 cf.data_type as data_type
578FROM civicrm_custom_group cg,
579 civicrm_custom_field cf
580WHERE cf.custom_group_id = cg.id
581AND cf.id IN ( $fieldIDList )
582";
583
584 $dao = CRM_Core_DAO::executeQuery($sql);
be2fb01f 585 $cvParams = [];
6a488035
TO
586
587 while ($dao->fetch()) {
588 $dataType = $dao->data_type == 'Date' ? 'Timestamp' : $dao->data_type;
589 foreach ($fieldValues[$dao->cf_id] as $fieldValue) {
3766bd36
CW
590 // Serialize array values
591 if (is_array($fieldValue['value']) && CRM_Core_BAO_CustomField::isSerialized($dao)) {
592 $fieldValue['value'] = CRM_Utils_Array::implodePadded($fieldValue['value']);
593 }
6a488035
TO
594 // Format null values correctly
595 if ($fieldValue['value'] === NULL || $fieldValue['value'] === '') {
596 switch ($dataType) {
597 case 'String':
598 case 'Int':
599 case 'Link':
600 case 'Boolean':
601 $fieldValue['value'] = '';
602 break;
603
604 case 'Timestamp':
605 $fieldValue['value'] = NULL;
606 break;
607
608 case 'StateProvince':
609 case 'Country':
610 case 'Money':
611 case 'Float':
2aa397bc 612 $fieldValue['value'] = (int) 0;
6a488035
TO
613 break;
614 }
615 }
616 // Ensure that value is of the right data type
617 elseif (CRM_Utils_Type::escape($fieldValue['value'], $dataType, FALSE) === NULL) {
618 return CRM_Core_Error::createAPIError(ts('value: %1 is not of the right field data type: %2',
be2fb01f 619 [
353ffa53
TO
620 1 => $fieldValue['value'],
621 2 => $dao->data_type,
be2fb01f 622 ]
353ffa53 623 ));
6a488035
TO
624 }
625
be2fb01f 626 $cvParam = [
6a488035
TO
627 'entity_id' => $params['entityID'],
628 'value' => $fieldValue['value'],
629 'type' => $dataType,
630 'custom_field_id' => $dao->cf_id,
631 'custom_group_id' => $dao->cg_id,
632 'table_name' => $dao->table_name,
633 'column_name' => $dao->column_name,
634 'is_multiple' => $dao->is_multiple,
63b7d442 635 'extends' => $dao->extends,
be2fb01f 636 ];
6a488035 637
5013100c 638 if (!empty($params['id'])) {
639 $cvParam['id'] = $params['id'];
640 }
641
e7dcccf0
CW
642 if ($cvParam['type'] == 'File') {
643 $cvParam['file_id'] = $fieldValue['value'];
644 }
645
6a488035 646 if (!array_key_exists($dao->table_name, $cvParams)) {
be2fb01f 647 $cvParams[$dao->table_name] = [];
6a488035
TO
648 }
649
650 if (!array_key_exists($fieldValue['id'], $cvParams[$dao->table_name])) {
be2fb01f 651 $cvParams[$dao->table_name][$fieldValue['id']] = [];
6a488035
TO
652 }
653
654 if ($fieldValue['id'] > 0) {
655 $cvParam['id'] = $fieldValue['id'];
656 }
657 $cvParams[$dao->table_name][$fieldValue['id']][] = $cvParam;
658 }
659 }
660
661 if (!empty($cvParams)) {
662 self::create($cvParams);
be2fb01f 663 return ['is_error' => 0, 'result' => 1];
6a488035
TO
664 }
665
666 return CRM_Core_Error::createAPIError(ts('Unknown error'));
667 }
668
669 /**
100fef9d 670 * Take in an array of entityID, custom_ID
6a488035
TO
671 * and gets the value from the appropriate table.
672 *
673 * To get the values of custom fields with IDs 13 and 43 for contact ID 1327, use:
674 * $params = array( 'entityID' => 1327, 'custom_13' => 1, 'custom_43' => 1 );
675 *
b44e3f84 676 * Entity Type will be inferred by the custom fields you request
6a488035
TO
677 * Specify $params['entityType'] if you do not supply any custom fields to return
678 * and entity type is other than Contact
679 *
680 * @array $params
681 *
c490a46a 682 * @param array $params
2a6da8d7
EM
683 *
684 * @throws Exception
6a488035 685 * @return array
6a488035 686 */
00be9182 687 public static function &getValues(&$params) {
6a488035
TO
688 if (empty($params)) {
689 return NULL;
690 }
691 if (!isset($params['entityID']) ||
692 CRM_Utils_Type::escape($params['entityID'],
693 'Integer', FALSE
694 ) === NULL
695 ) {
696 return CRM_Core_Error::createAPIError(ts('entityID needs to be set and of type Integer'));
697 }
698
699 // first collect all the ids. The format is:
700 // custom_ID
be2fb01f 701 $fieldIDs = [];
6a488035
TO
702 foreach ($params as $n => $v) {
703 $key = $idx = NULL;
704 if (substr($n, 0, 7) == 'custom_') {
705 $idx = substr($n, 7);
706 if (CRM_Utils_Type::escape($idx, 'Integer', FALSE) === NULL) {
707 return CRM_Core_Error::createAPIError(ts('field ID needs to be of type Integer for index %1',
be2fb01f 708 [1 => $idx]
353ffa53 709 ));
6a488035
TO
710 }
711 $fieldIDs[] = (int ) $idx;
712 }
713 }
714
be2fb01f 715 $default = ['Contact', 'Individual', 'Household', 'Organization'];
6a488035
TO
716 if (!($type = CRM_Utils_Array::value('entityType', $params)) ||
717 in_array($params['entityType'], $default)
718 ) {
719 $type = NULL;
720 }
721 else {
722 $entities = CRM_Core_SelectValues::customGroupExtends();
723 if (!array_key_exists($type, $entities)) {
724 if (in_array($type, $entities)) {
725 $type = $entities[$type];
726 if (in_array($type, $default)) {
727 $type = NULL;
728 }
729 }
730 else {
731 return CRM_Core_Error::createAPIError(ts('Invalid entity type') . ': "' . $type . '"');
732 }
733 }
734 }
735
736 $values = self::getEntityValues($params['entityID'],
737 $type,
738 $fieldIDs
739 );
740 if (empty($values)) {
741 // note that this behaviour is undesirable from an API point of view - it should return an empty array
742 // since this is also called by the merger code & not sure the consequences of changing
743 // are just handling undoing this in the api layer. ie. converting the error back into a success
be2fb01f 744 $result = [
6a488035
TO
745 'is_error' => 1,
746 'error_message' => 'No values found for the specified entity ID and custom field(s).',
be2fb01f 747 ];
6a488035
TO
748 return $result;
749 }
750 else {
be2fb01f 751 $result = [
6a488035
TO
752 'is_error' => 0,
753 'entityID' => $params['entityID'],
be2fb01f 754 ];
6a488035
TO
755 foreach ($values as $id => $value) {
756 $result["custom_{$id}"] = $value;
757 }
758 return $result;
759 }
760 }
96025800 761
6a488035 762}