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