Merge pull request #14091 from civicrm/5.13
[civicrm-core.git] / CRM / Core / BAO / CustomValueTable.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2019
32 * $Id$
33 *
34 */
35 class CRM_Core_BAO_CustomValueTable {
36
37 /**
38 * @param array $customParams
39 *
40 * @throws Exception
41 */
42 public static function create(&$customParams) {
43 if (empty($customParams) ||
44 !is_array($customParams)
45 ) {
46 return;
47 }
48
49 $paramFieldsExtendContactForEntities = [];
50
51 foreach ($customParams as $tableName => $tables) {
52 foreach ($tables as $index => $fields) {
53 $sqlOP = NULL;
54 $hookID = NULL;
55 $hookOP = NULL;
56 $entityID = NULL;
57 $isMultiple = FALSE;
58 $set = [];
59 $params = [];
60 $count = 1;
61 foreach ($fields as $field) {
62 if (!$sqlOP) {
63 $entityID = $field['entity_id'];
64 $hookID = $field['custom_group_id'];
65 $isMultiple = $field['is_multiple'];
66 if (array_key_exists('id', $field)) {
67 $sqlOP = "UPDATE $tableName ";
68 $where = " WHERE id = %{$count}";
69 $params[$count] = [$field['id'], 'Integer'];
70 $count++;
71 $hookOP = 'edit';
72 }
73 else {
74 $sqlOP = "INSERT INTO $tableName ";
75 $where = NULL;
76 $hookOP = 'create';
77 }
78 }
79
80 // fix the value before we store it
81 $value = $field['value'];
82 $type = $field['type'];
83 switch ($type) {
84 case 'StateProvince':
85 $type = 'Integer';
86 if (is_array($value)) {
87 $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $value) . CRM_Core_DAO::VALUE_SEPARATOR;
88 $type = 'String';
89 }
90 elseif (!is_numeric($value) && !strstr($value, CRM_Core_DAO::VALUE_SEPARATOR)) {
91 //fix for multi select state, CRM-3437
92 $mulValues = explode(',', $value);
93 $validStates = [];
94 foreach ($mulValues as $key => $stateVal) {
95 $states = [];
96 $states['state_province'] = trim($stateVal);
97
98 CRM_Utils_Array::lookupValue($states, 'state_province',
99 CRM_Core_PseudoConstant::stateProvince(), TRUE
100 );
101 if (empty($states['state_province_id'])) {
102 CRM_Utils_Array::lookupValue($states, 'state_province',
103 CRM_Core_PseudoConstant::stateProvinceAbbreviation(), TRUE
104 );
105 }
106 $validStates[] = CRM_Utils_Array::value('state_province_id', $states);
107 }
108 $value = implode(CRM_Core_DAO::VALUE_SEPARATOR,
109 $validStates
110 );
111 $type = 'String';
112 }
113 elseif (!$value) {
114 // CRM-3415
115 // using type of timestamp allows us to sneak in a null into db
116 // gross but effective hack
117 $value = NULL;
118 $type = 'Timestamp';
119 }
120 else {
121 $type = 'String';
122 }
123 break;
124
125 case 'Country':
126 $type = 'Integer';
127 if (is_array($value)) {
128 $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $value) . CRM_Core_DAO::VALUE_SEPARATOR;
129 $type = 'String';
130 }
131 elseif (!is_numeric($value) && !strstr($value, CRM_Core_DAO::VALUE_SEPARATOR)) {
132 //fix for multi select country, CRM-3437
133 $mulValues = explode(',', $value);
134 $validCountries = [];
135 foreach ($mulValues as $key => $countryVal) {
136 $countries = [];
137 $countries['country'] = trim($countryVal);
138 CRM_Utils_Array::lookupValue($countries, 'country',
139 CRM_Core_PseudoConstant::country(), TRUE
140 );
141 if (empty($countries['country_id'])) {
142 CRM_Utils_Array::lookupValue($countries, 'country',
143 CRM_Core_PseudoConstant::countryIsoCode(), TRUE
144 );
145 }
146 $validCountries[] = CRM_Utils_Array::value('country_id', $countries);
147 }
148 $value = implode(CRM_Core_DAO::VALUE_SEPARATOR,
149 $validCountries
150 );
151 $type = 'String';
152 }
153 elseif (!$value) {
154 // CRM-3415
155 // using type of timestamp allows us to sneak in a null into db
156 // gross but effective hack
157 $value = NULL;
158 $type = 'Timestamp';
159 }
160 else {
161 $type = 'String';
162 }
163 break;
164
165 case 'File':
166 if (!$field['file_id']) {
167 CRM_Core_Error::fatal();
168 }
169
170 // need to add/update civicrm_entity_file
171 $entityFileDAO = new CRM_Core_DAO_EntityFile();
172 $entityFileDAO->file_id = $field['file_id'];
173 $entityFileDAO->find(TRUE);
174
175 $entityFileDAO->entity_table = $field['table_name'];
176 $entityFileDAO->entity_id = $field['entity_id'];
177 $entityFileDAO->file_id = $field['file_id'];
178 $entityFileDAO->save();
179 $entityFileDAO->free();
180 $value = $field['file_id'];
181 $type = 'String';
182 break;
183
184 case 'Date':
185 $value = CRM_Utils_Date::isoToMysql($value);
186 break;
187
188 case 'Int':
189 if (is_numeric($value)) {
190 $type = 'Integer';
191 }
192 else {
193 $type = 'Timestamp';
194 }
195 break;
196
197 case 'ContactReference':
198 if ($value == NULL) {
199 $type = 'Timestamp';
200 }
201 else {
202 $type = 'Integer';
203 }
204 break;
205
206 case 'RichTextEditor':
207 $type = 'String';
208 break;
209
210 case 'Boolean':
211 //fix for CRM-3290
212 $value = CRM_Utils_String::strtoboolstr($value);
213 if ($value === FALSE) {
214 $type = 'Timestamp';
215 }
216 break;
217
218 default:
219 break;
220 }
221 if ($value === 'null') {
222 // when unsetting a value to null, we don't need to validate the type
223 // https://projectllr.atlassian.net/browse/VGQBMP-20
224 $set[$field['column_name']] = $value;
225 }
226 else {
227 $set[$field['column_name']] = "%{$count}";
228 $params[$count] = [$value, $type];
229 $count++;
230 }
231
232 $fieldExtends = CRM_Utils_Array::value('extends', $field);
233 if (
234 CRM_Utils_Array::value('entity_table', $field) == 'civicrm_contact'
235 || $fieldExtends == 'Contact'
236 || $fieldExtends == 'Individual'
237 || $fieldExtends == 'Organization'
238 || $fieldExtends == 'Household'
239 ) {
240 $paramFieldsExtendContactForEntities[$entityID]['custom_' . CRM_Utils_Array::value('custom_field_id', $field)] = CRM_Utils_Array::value('custom_field_id', $field);
241 }
242 }
243
244 if (!empty($set)) {
245 $setClause = [];
246 foreach ($set as $n => $v) {
247 $setClause[] = "$n = $v";
248 }
249 $setClause = implode(',', $setClause);
250 if (!$where) {
251 // do this only for insert
252 $set['entity_id'] = "%{$count}";
253 $params[$count] = [$entityID, 'Integer'];
254 $count++;
255
256 $fieldNames = implode(',', CRM_Utils_Type::escapeAll(array_keys($set), 'MysqlColumnNameOrAlias'));
257 $fieldValues = implode(',', array_values($set));
258 $query = "$sqlOP ( $fieldNames ) VALUES ( $fieldValues )";
259 // for multiple values we dont do on duplicate key update
260 if (!$isMultiple) {
261 $query .= " ON DUPLICATE KEY UPDATE $setClause";
262 }
263 }
264 else {
265 $query = "$sqlOP SET $setClause $where";
266 }
267 $dao = CRM_Core_DAO::executeQuery($query, $params);
268
269 CRM_Utils_Hook::custom($hookOP,
270 $hookID,
271 $entityID,
272 $fields
273 );
274 }
275 }
276 }
277
278 if (!empty($paramFieldsExtendContactForEntities)) {
279 CRM_Contact_BAO_Contact::updateGreetingsOnTokenFieldChange($paramFieldsExtendContactForEntities, ['contact_id' => $entityID]);
280 }
281 }
282
283 /**
284 * Given a field return the mysql data type associated with it.
285 *
286 * @param string $type
287 * @param int $maxLength
288 *
289 * @return string
290 * the mysql data store placeholder
291 */
292 public static function fieldToSQLType($type, $maxLength = 255) {
293 if (!isset($maxLength) ||
294 !is_numeric($maxLength) ||
295 $maxLength <= 0
296 ) {
297 $maxLength = 255;
298 }
299
300 switch ($type) {
301 case 'String':
302 case 'Link':
303 return "varchar($maxLength)";
304
305 case 'Boolean':
306 return 'tinyint';
307
308 case 'Int':
309 return 'int';
310
311 // the below three are FK's, and have constraints added to them
312
313 case 'ContactReference':
314 case 'StateProvince':
315 case 'Country':
316 case 'File':
317 return 'int unsigned';
318
319 case 'Float':
320 return 'double';
321
322 case 'Money':
323 return 'decimal(20,2)';
324
325 case 'Memo':
326 case 'RichTextEditor':
327 return 'text';
328
329 case 'Date':
330 return 'datetime';
331
332 default:
333 CRM_Core_Error::fatal();
334 }
335 }
336
337 /**
338 * @param array $params
339 * @param $entityTable
340 * @param int $entityID
341 */
342 public static function store(&$params, $entityTable, $entityID) {
343 $cvParams = [];
344 foreach ($params as $fieldID => $param) {
345 foreach ($param as $index => $customValue) {
346 $cvParam = [
347 'entity_table' => $entityTable,
348 'entity_id' => $entityID,
349 'value' => $customValue['value'],
350 'type' => $customValue['type'],
351 'custom_field_id' => $customValue['custom_field_id'],
352 'custom_group_id' => $customValue['custom_group_id'],
353 'table_name' => $customValue['table_name'],
354 'column_name' => $customValue['column_name'],
355 'is_multiple' => CRM_Utils_Array::value('is_multiple', $customValue),
356 'file_id' => $customValue['file_id'],
357 ];
358
359 // Fix Date type to be timestamp, since that is how we store in db.
360 if ($cvParam['type'] == 'Date') {
361 $cvParam['type'] = 'Timestamp';
362 }
363
364 if (!empty($customValue['id'])) {
365 $cvParam['id'] = $customValue['id'];
366 }
367 if (!array_key_exists($customValue['table_name'], $cvParams)) {
368 $cvParams[$customValue['table_name']] = [];
369 }
370
371 if (!array_key_exists($index, $cvParams[$customValue['table_name']])) {
372 $cvParams[$customValue['table_name']][$index] = [];
373 }
374
375 $cvParams[$customValue['table_name']][$index][] = $cvParam;
376 }
377 }
378 if (!empty($cvParams)) {
379 self::create($cvParams);
380 }
381 }
382
383 /**
384 * Post process function.
385 *
386 * @param array $params
387 * @param $entityTable
388 * @param int $entityID
389 * @param $customFieldExtends
390 */
391 public static function postProcess(&$params, $entityTable, $entityID, $customFieldExtends) {
392 $customData = CRM_Core_BAO_CustomField::postProcess($params,
393 $entityID,
394 $customFieldExtends
395 );
396
397 if (!empty($customData)) {
398 self::store($customData, $entityTable, $entityID);
399 }
400 }
401
402 /**
403 * Return an array of all custom values associated with an entity.
404 *
405 * @param int $entityID
406 * Identification number of the entity.
407 * @param string $entityType
408 * Type of entity that the entityID corresponds to, specified.
409 * as a string with format "'<EntityName>'". Comma separated
410 * list may be used to specify OR matches. Allowable values
411 * are enumerated types in civicrm_custom_group.extends field.
412 * Optional. Default value assumes entityID references a
413 * contact entity.
414 * @param array $fieldIDs
415 * Optional list of fieldIDs that we want to retrieve. If this.
416 * is set the entityType is ignored
417 *
418 * @param bool $formatMultiRecordField
419 * @param array $DTparams - CRM-17810 dataTable params for the multiValued custom fields.
420 *
421 * @return array
422 * Array of custom values for the entity with key=>value
423 * pairs specified as civicrm_custom_field.id => custom value.
424 * Empty array if no custom values found.
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 CRM_Core_Error::fatal('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] = $dao->is_multiple ? TRUE : FALSE;
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.data_type as data_type
584 FROM civicrm_custom_group cg,
585 civicrm_custom_field cf
586 WHERE cf.custom_group_id = cg.id
587 AND cf.id IN ( $fieldIDList )
588 ";
589
590 $dao = CRM_Core_DAO::executeQuery($sql);
591 $cvParams = [];
592
593 while ($dao->fetch()) {
594 $dataType = $dao->data_type == 'Date' ? 'Timestamp' : $dao->data_type;
595 foreach ($fieldValues[$dao->cf_id] as $fieldValue) {
596 // Format null values correctly
597 if ($fieldValue['value'] === NULL || $fieldValue['value'] === '') {
598 switch ($dataType) {
599 case 'String':
600 case 'Int':
601 case 'Link':
602 case 'Boolean':
603 $fieldValue['value'] = '';
604 break;
605
606 case 'Timestamp':
607 $fieldValue['value'] = NULL;
608 break;
609
610 case 'StateProvince':
611 case 'Country':
612 case 'Money':
613 case 'Float':
614 $fieldValue['value'] = (int) 0;
615 break;
616 }
617 }
618 // Ensure that value is of the right data type
619 elseif (CRM_Utils_Type::escape($fieldValue['value'], $dataType, FALSE) === NULL) {
620 return CRM_Core_Error::createAPIError(ts('value: %1 is not of the right field data type: %2',
621 [
622 1 => $fieldValue['value'],
623 2 => $dao->data_type,
624 ]
625 ));
626 }
627
628 $cvParam = [
629 'entity_id' => $params['entityID'],
630 'value' => $fieldValue['value'],
631 'type' => $dataType,
632 'custom_field_id' => $dao->cf_id,
633 'custom_group_id' => $dao->cg_id,
634 'table_name' => $dao->table_name,
635 'column_name' => $dao->column_name,
636 'is_multiple' => $dao->is_multiple,
637 'extends' => $dao->extends,
638 ];
639
640 if (!empty($params['id'])) {
641 $cvParam['id'] = $params['id'];
642 }
643
644 if ($cvParam['type'] == 'File') {
645 $cvParam['file_id'] = $fieldValue['value'];
646 }
647
648 if (!array_key_exists($dao->table_name, $cvParams)) {
649 $cvParams[$dao->table_name] = [];
650 }
651
652 if (!array_key_exists($fieldValue['id'], $cvParams[$dao->table_name])) {
653 $cvParams[$dao->table_name][$fieldValue['id']] = [];
654 }
655
656 if ($fieldValue['id'] > 0) {
657 $cvParam['id'] = $fieldValue['id'];
658 }
659 $cvParams[$dao->table_name][$fieldValue['id']][] = $cvParam;
660 }
661 }
662
663 if (!empty($cvParams)) {
664 self::create($cvParams);
665 return ['is_error' => 0, 'result' => 1];
666 }
667
668 return CRM_Core_Error::createAPIError(ts('Unknown error'));
669 }
670
671 /**
672 * Take in an array of entityID, custom_ID
673 * and gets the value from the appropriate table.
674 *
675 * To get the values of custom fields with IDs 13 and 43 for contact ID 1327, use:
676 * $params = array( 'entityID' => 1327, 'custom_13' => 1, 'custom_43' => 1 );
677 *
678 * Entity Type will be inferred by the custom fields you request
679 * Specify $params['entityType'] if you do not supply any custom fields to return
680 * and entity type is other than Contact
681 *
682 * @array $params
683 *
684 * @param array $params
685 *
686 * @throws Exception
687 * @return array
688 */
689 public static function &getValues(&$params) {
690 if (empty($params)) {
691 return NULL;
692 }
693 if (!isset($params['entityID']) ||
694 CRM_Utils_Type::escape($params['entityID'],
695 'Integer', FALSE
696 ) === NULL
697 ) {
698 return CRM_Core_Error::createAPIError(ts('entityID needs to be set and of type Integer'));
699 }
700
701 // first collect all the ids. The format is:
702 // custom_ID
703 $fieldIDs = [];
704 foreach ($params as $n => $v) {
705 $key = $idx = NULL;
706 if (substr($n, 0, 7) == 'custom_') {
707 $idx = substr($n, 7);
708 if (CRM_Utils_Type::escape($idx, 'Integer', FALSE) === NULL) {
709 return CRM_Core_Error::createAPIError(ts('field ID needs to be of type Integer for index %1',
710 [1 => $idx]
711 ));
712 }
713 $fieldIDs[] = (int ) $idx;
714 }
715 }
716
717 $default = ['Contact', 'Individual', 'Household', 'Organization'];
718 if (!($type = CRM_Utils_Array::value('entityType', $params)) ||
719 in_array($params['entityType'], $default)
720 ) {
721 $type = NULL;
722 }
723 else {
724 $entities = CRM_Core_SelectValues::customGroupExtends();
725 if (!array_key_exists($type, $entities)) {
726 if (in_array($type, $entities)) {
727 $type = $entities[$type];
728 if (in_array($type, $default)) {
729 $type = NULL;
730 }
731 }
732 else {
733 return CRM_Core_Error::createAPIError(ts('Invalid entity type') . ': "' . $type . '"');
734 }
735 }
736 }
737
738 $values = self::getEntityValues($params['entityID'],
739 $type,
740 $fieldIDs
741 );
742 if (empty($values)) {
743 // note that this behaviour is undesirable from an API point of view - it should return an empty array
744 // since this is also called by the merger code & not sure the consequences of changing
745 // are just handling undoing this in the api layer. ie. converting the error back into a success
746 $result = [
747 'is_error' => 1,
748 'error_message' => 'No values found for the specified entity ID and custom field(s).',
749 ];
750 return $result;
751 }
752 else {
753 $result = [
754 'is_error' => 0,
755 'entityID' => $params['entityID'],
756 ];
757 foreach ($values as $id => $value) {
758 $result["custom_{$id}"] = $value;
759 }
760 return $result;
761 }
762 }
763
764 }