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