CRM-13823 - Add php version check
[civicrm-core.git] / CRM / Core / DAO.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
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 +--------------------------------------------------------------------+
ced9bfed 26 */
6a488035
TO
27
28/**
29 * Our base DAO class. All DAO classes should inherit from this class.
30 *
31 * @package CRM
e7112fa7 32 * @copyright CiviCRM LLC (c) 2004-2015
6a488035
TO
33 */
34
35require_once 'PEAR.php';
36require_once 'DB/DataObject.php';
37
38require_once 'CRM/Core/I18n.php';
28518c90
EM
39
40/**
41 * Class CRM_Core_DAO
42 */
6a488035
TO
43class CRM_Core_DAO extends DB_DataObject {
44
45 /**
100fef9d 46 * A null object so we can pass it as reference if / when needed
6a488035
TO
47 */
48 static $_nullObject = NULL;
49 static $_nullArray = array();
50
51 static $_dbColumnValueCache = NULL;
7da04cde 52 const NOT_NULL = 1, IS_NULL = 2,
353ffa53
TO
53 DB_DAO_NOTNULL = 128,
54 VALUE_SEPARATOR = "\ 1",
55 BULK_INSERT_COUNT = 200,
56 BULK_INSERT_HIGH_COUNT = 200,
57 // special value for mail bulk inserts to avoid
58 // potential duplication, assuming a smaller number reduces number of queries
59 // by some factor, so some tradeoff. CRM-8678
60 BULK_MAIL_INSERT_COUNT = 10,
61 QUERY_FORMAT_WILDCARD = 1,
62 QUERY_FORMAT_NO_QUOTES = 2;
887a4028 63
d424ffde 64 /**
6a488035 65 * Define entities that shouldn't be created or deleted when creating/ deleting
d424ffde
CW
66 * test objects - this prevents world regions, countries etc from being added / deleted
67 * @var array
6a488035
TO
68 */
69 static $_testEntitiesToSkip = array();
70 /**
fe482240 71 * The factory class for this application.
6a488035
TO
72 * @var object
73 */
74 static $_factory = NULL;
75
aca2de91
CW
76 static $_checkedSqlFunctionsExist = FALSE;
77
6a488035 78 /**
fe482240 79 * Class constructor.
6a488035 80 *
77b97be7 81 * @return \CRM_Core_DAO
6a488035 82 */
00be9182 83 public function __construct() {
6a488035
TO
84 $this->initialize();
85 $this->__table = $this->getTableName();
86 }
87
88 /**
fe482240 89 * Empty definition for virtual function.
6a488035 90 */
00be9182 91 public static function getTableName() {
6a488035
TO
92 return NULL;
93 }
94
95 /**
fe482240 96 * Initialize the DAO object.
6a488035 97 *
6a0b768e
TO
98 * @param string $dsn
99 * The database connection string.
6a488035 100 */
00be9182 101 public static function init($dsn) {
6a488035
TO
102 $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
103 $options['database'] = $dsn;
104 if (defined('CIVICRM_DAO_DEBUG')) {
105 self::DebugLevel(CIVICRM_DAO_DEBUG);
106 }
635f0b86
TO
107 CRM_Core_DAO::setFactory(new CRM_Contact_DAO_Factory());
108 if (CRM_Utils_Constant::value('CIVICRM_MYSQL_STRICT', CRM_Utils_System::isDevelopment())) {
109 CRM_Core_DAO::executeQuery('SET SESSION sql_mode = STRICT_TRANS_TABLES');
110 }
6a488035
TO
111 }
112
e1b64aab 113 /**
100fef9d 114 * @param string $fieldName
e1b64aab 115 * @param $fieldDef
c490a46a 116 * @param array $params
e1b64aab 117 */
e79cd558 118 protected function assignTestFK($fieldName, $fieldDef, $params) {
e1b64aab
TO
119 $required = CRM_Utils_Array::value('required', $fieldDef);
120 $FKClassName = CRM_Utils_Array::value('FKClassName', $fieldDef);
121 $dbName = $fieldDef['name'];
2444854d 122 $daoName = str_replace('_BAO_', '_DAO_', get_class($this));
e1b64aab
TO
123
124 // skip the FK if it is not required
125 // if it's contact id we should create even if not required
126 // we'll have a go @ fetching first though
127 // we WILL create campaigns though for so tests with a campaign pseudoconstant will complete
128 if ($FKClassName === 'CRM_Campaign_DAO_Campaign' && $daoName != $FKClassName) {
129 $required = TRUE;
130 }
131 if (!$required && $dbName != 'contact_id') {
795492f3 132 $fkDAO = new $FKClassName();
e1b64aab 133 if ($fkDAO->find(TRUE)) {
e79cd558 134 $this->$dbName = $fkDAO->id;
e1b64aab
TO
135 }
136 unset($fkDAO);
137 }
138
139 elseif (in_array($FKClassName, CRM_Core_DAO::$_testEntitiesToSkip)) {
140 $depObject = new $FKClassName();
141 $depObject->find(TRUE);
e79cd558 142 $this->$dbName = $depObject->id;
e1b64aab
TO
143 unset($depObject);
144 }
145 elseif ($daoName == 'CRM_Member_DAO_MembershipType' && $fieldName == 'member_of_contact_id') {
146 // FIXME: the fields() metadata is not specific enough
147 $depObject = CRM_Core_DAO::createTestObject($FKClassName, array('contact_type' => 'Organization'));
e79cd558 148 $this->$dbName = $depObject->id;
e1b64aab
TO
149 unset($depObject);
150 }
151 else {
152 //if it is required we need to generate the dependency object first
153 $depObject = CRM_Core_DAO::createTestObject($FKClassName, CRM_Utils_Array::value($dbName, $params, 1));
e79cd558 154 $this->$dbName = $depObject->id;
e1b64aab
TO
155 unset($depObject);
156 }
157 }
158
159 /**
e79cd558
TO
160 * Generate and assign an arbitrary value to a field of a test object.
161 *
162 * @param string $fieldName
163 * @param array $fieldDef
6a0b768e
TO
164 * @param int $counter
165 * The globally-unique ID of the test object.
e1b64aab 166 */
e79cd558
TO
167 protected function assignTestValue($fieldName, &$fieldDef, $counter) {
168 $dbName = $fieldDef['name'];
169 $daoName = get_class($this);
e1b64aab
TO
170 $handled = FALSE;
171
e79cd558 172 if (!$handled && $dbName == 'contact_sub_type') {
e1b64aab
TO
173 //coming up with a rule to set this is too complex let's not set it
174 $handled = TRUE;
175 }
176
177 // Pick an option value if needed
178 if (!$handled && $fieldDef['type'] !== CRM_Utils_Type::T_BOOLEAN) {
179 $options = $daoName::buildOptions($dbName, 'create');
180 if ($options) {
e79cd558 181 $this->$dbName = key($options);
e1b64aab
TO
182 $handled = TRUE;
183 }
184 }
185
186 if (!$handled) {
187 switch ($fieldDef['type']) {
188 case CRM_Utils_Type::T_INT:
189 case CRM_Utils_Type::T_FLOAT:
190 case CRM_Utils_Type::T_MONEY:
191 if (isset($fieldDef['precision'])) {
192 // $object->$dbName = CRM_Utils_Number::createRandomDecimal($value['precision']);
e79cd558 193 $this->$dbName = CRM_Utils_Number::createTruncatedDecimal($counter, $fieldDef['precision']);
e1b64aab
TO
194 }
195 else {
e79cd558 196 $this->$dbName = $counter;
e1b64aab
TO
197 }
198 break;
199
200 case CRM_Utils_Type::T_BOOLEAN:
201 if (isset($fieldDef['default'])) {
e79cd558 202 $this->$dbName = $fieldDef['default'];
e1b64aab
TO
203 }
204 elseif ($fieldDef['name'] == 'is_deleted' || $fieldDef['name'] == 'is_test') {
e79cd558 205 $this->$dbName = 0;
e1b64aab
TO
206 }
207 else {
e79cd558 208 $this->$dbName = 1;
e1b64aab
TO
209 }
210 break;
211
212 case CRM_Utils_Type::T_DATE:
213 case CRM_Utils_Type::T_TIMESTAMP:
214 case CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME:
e79cd558 215 $this->$dbName = '19700101';
e1b64aab
TO
216 if ($dbName == 'end_date') {
217 // put this in the future
e79cd558 218 $this->$dbName = '20200101';
e1b64aab
TO
219 }
220 break;
221
222 case CRM_Utils_Type::T_TIME:
5d6aaf6b 223 CRM_Core_Error::fatal("T_TIME shouldn't be used.");
795492f3
TO
224 //$object->$dbName='000000';
225 //break;
e1b64aab 226 case CRM_Utils_Type::T_CCNUM:
e79cd558 227 $this->$dbName = '4111 1111 1111 1111';
e1b64aab
TO
228 break;
229
230 case CRM_Utils_Type::T_URL:
e79cd558 231 $this->$dbName = 'http://www.civicrm.org';
e1b64aab
TO
232 break;
233
234 case CRM_Utils_Type::T_STRING:
235 case CRM_Utils_Type::T_BLOB:
236 case CRM_Utils_Type::T_MEDIUMBLOB:
237 case CRM_Utils_Type::T_TEXT:
238 case CRM_Utils_Type::T_LONGTEXT:
239 case CRM_Utils_Type::T_EMAIL:
240 default:
241 // WAS: if (isset($value['enumValues'])) {
242 // TODO: see if this works with all pseudoconstants
243 if (isset($fieldDef['pseudoconstant'], $fieldDef['pseudoconstant']['callback'])) {
244 if (isset($fieldDef['default'])) {
e79cd558 245 $this->$dbName = $fieldDef['default'];
e1b64aab
TO
246 }
247 else {
248 $options = CRM_Core_PseudoConstant::get($daoName, $fieldName);
249 if (is_array($options)) {
e79cd558 250 $this->$dbName = $options[0];
e1b64aab
TO
251 }
252 else {
253 $defaultValues = explode(',', $options);
e79cd558 254 $this->$dbName = $defaultValues[0];
e1b64aab
TO
255 }
256 }
257 }
258 else {
e79cd558 259 $this->$dbName = $dbName . '_' . $counter;
e1b64aab 260 $maxlength = CRM_Utils_Array::value('maxlength', $fieldDef);
e79cd558
TO
261 if ($maxlength > 0 && strlen($this->$dbName) > $maxlength) {
262 $this->$dbName = substr($this->$dbName, 0, $fieldDef['maxlength']);
e1b64aab
TO
263 }
264 }
265 }
266 }
267 }
268
6a488035 269 /**
8eedd10a 270 * Reset the DAO object.
6a488035 271 *
8eedd10a 272 * DAO is kinda crappy in that there is an unwritten rule of one query per DAO.
273 *
274 * We attempt to get around this crappy restriction by resetting some of DAO's internal fields. Use this with caution
6a488035 275 */
00be9182 276 public function reset() {
6a488035
TO
277
278 foreach (array_keys($this->table()) as $field) {
279 unset($this->$field);
280 }
281
282 /**
283 * reset the various DB_DAO structures manually
284 */
285 $this->_query = array();
286 $this->whereAdd();
287 $this->selectAdd();
288 $this->joinAdd();
289 }
290
a0ee3941 291 /**
100fef9d 292 * @param string $tableName
a0ee3941
EM
293 *
294 * @return string
295 */
00be9182 296 public static function getLocaleTableName($tableName) {
6a488035
TO
297 global $dbLocale;
298 if ($dbLocale) {
299 $tables = CRM_Core_I18n_Schema::schemaStructureTables();
300 if (in_array($tableName, $tables)) {
301 return $tableName . $dbLocale;
302 }
303 }
304 return $tableName;
305 }
306
307 /**
308 * Execute a query by the current DAO, localizing it along the way (if needed).
309 *
6a0b768e
TO
310 * @param string $query
311 * The SQL query for execution.
312 * @param bool $i18nRewrite
313 * Whether to rewrite the query.
6a488035 314 *
a6c01b45
CW
315 * @return object
316 * the current DAO object after the query execution
6a488035 317 */
00be9182 318 public function query($query, $i18nRewrite = TRUE) {
6a488035
TO
319 // rewrite queries that should use $dbLocale-based views for multi-language installs
320 global $dbLocale;
321 if ($i18nRewrite and $dbLocale) {
322 $query = CRM_Core_I18n_Schema::rewriteQuery($query);
323 }
324
325 return parent::query($query);
326 }
327
328 /**
329 * Static function to set the factory instance for this class.
330 *
6a0b768e
TO
331 * @param object $factory
332 * The factory application object.
6a488035 333 */
00be9182 334 public static function setFactory(&$factory) {
6a488035
TO
335 self::$_factory = &$factory;
336 }
337
338 /**
339 * Factory method to instantiate a new object from a table name.
340 *
da6b46f4 341 * @param string $table
6a488035 342 */
00be9182 343 public function factory($table = '') {
6a488035
TO
344 if (!isset(self::$_factory)) {
345 return parent::factory($table);
346 }
347
348 return self::$_factory->create($table);
349 }
350
351 /**
352 * Initialization for all DAO objects. Since we access DB_DO programatically
353 * we need to set the links manually.
6a488035 354 */
00be9182 355 public function initialize() {
6a488035 356 $this->_connect();
086c354c 357 $this->query("SET NAMES utf8");
6a488035
TO
358 }
359
360 /**
361 * Defines the default key as 'id'.
362 *
6a488035
TO
363 *
364 * @return array
365 */
00be9182 366 public function keys() {
6a488035
TO
367 static $keys;
368 if (!isset($keys)) {
369 $keys = array('id');
370 }
371 return $keys;
372 }
373
374 /**
375 * Tells DB_DataObject which keys use autoincrement.
376 * 'id' is autoincrementing by default.
377 *
6a488035
TO
378 *
379 * @return array
380 */
00be9182 381 public function sequenceKey() {
6a488035
TO
382 static $sequenceKeys;
383 if (!isset($sequenceKeys)) {
384 $sequenceKeys = array('id', TRUE);
385 }
386 return $sequenceKeys;
387 }
388
389 /**
fe482240 390 * Returns list of FK relationships.
6a488035 391 *
6a488035 392 *
a6c01b45 393 * @return array
16b10e64 394 * Array of CRM_Core_Reference_Interface
6a488035 395 */
00be9182 396 public static function getReferenceColumns() {
71e5aa5c 397 return array();
6a488035
TO
398 }
399
400 /**
fe482240 401 * Returns all the column names of this table.
6a488035 402 *
6a488035
TO
403 *
404 * @return array
405 */
795492f3 406 public static function &fields() {
6a488035
TO
407 $result = NULL;
408 return $result;
409 }
410
b5c2afd0 411 /**
100fef9d 412 * Get/set an associative array of table columns
b5c2afd0 413 *
a6c01b45
CW
414 * @return array
415 * (associative)
b5c2afd0 416 */
00be9182 417 public function table() {
6a488035
TO
418 $fields = &$this->fields();
419
420 $table = array();
421 if ($fields) {
422 foreach ($fields as $name => $value) {
423 $table[$value['name']] = $value['type'];
a7488080 424 if (!empty($value['required'])) {
6a488035
TO
425 $table[$value['name']] += self::DB_DAO_NOTNULL;
426 }
427 }
428 }
429
6a488035
TO
430 return $table;
431 }
432
a0ee3941
EM
433 /**
434 * @return $this
435 */
9f35e05d 436 public function save($hook = TRUE) {
6a488035
TO
437 if (!empty($this->id)) {
438 $this->update();
8498c2b7 439
9f35e05d
TO
440 if ($hook) {
441 $event = new \Civi\Core\DAO\Event\PostUpdate($this);
442 \Civi::service('dispatcher')->dispatch("DAO::post-update", $event);
443 }
6a488035
TO
444 }
445 else {
446 $this->insert();
8498c2b7 447
9f35e05d
TO
448 if ($hook) {
449 $event = new \Civi\Core\DAO\Event\PostUpdate($this);
450 \Civi::service('dispatcher')->dispatch("DAO::post-insert", $event);
451 }
6a488035
TO
452 }
453 $this->free();
454
9f35e05d
TO
455 if ($hook) {
456 CRM_Utils_Hook::postSave($this);
457 }
6a488035
TO
458
459 return $this;
460 }
461
1cd3ffa9 462 /**
fe482240 463 * Deletes items from table which match current objects variables.
1cd3ffa9
EM
464 *
465 * Returns the true on success
466 *
467 * for example
468 *
469 * Designed to be extended
470 *
471 * $object = new mytable();
472 * $object->ID=123;
473 * echo $object->delete(); // builds a conditon
474 *
475 * $object = new mytable();
476 * $object->whereAdd('age > 12');
477 * $object->limit(1);
478 * $object->orderBy('age DESC');
479 * $object->delete(true); // dont use object vars, use the conditions, limit and order.
480 *
481 * @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
482 * we will build the condition only using the whereAdd's. Default is to
483 * build the condition only using the object parameters.
484 *
485 * * @return mixed Int (No. of rows affected) on success, false on failure, 0 on no data affected
486 */
00be9182 487 public function delete($useWhere = FALSE) {
97c4fe76 488 $result = parent::delete($useWhere);
8498c2b7 489
48d849b1 490 $event = new \Civi\Core\DAO\Event\PostDelete($this, $result);
048222df 491 \Civi::service('dispatcher')->dispatch("DAO::post-delete", $event);
8498c2b7 492
97c4fe76 493 return $result;
494 }
495
a0ee3941
EM
496 /**
497 * @param bool $created
498 */
00be9182 499 public function log($created = FALSE) {
6a488035
TO
500 static $cid = NULL;
501
502 if (!$this->getLog()) {
503 return;
504 }
505
506 if (!$cid) {
507 $session = CRM_Core_Session::singleton();
508 $cid = $session->get('userID');
509 }
510
511 // return is we dont have handle to FK
512 if (!$cid) {
513 return;
514 }
515
353ffa53
TO
516 $dao = new CRM_Core_DAO_Log();
517 $dao->entity_table = $this->getTableName();
518 $dao->entity_id = $this->id;
519 $dao->modified_id = $cid;
6a488035
TO
520 $dao->modified_date = date("YmdHis");
521 $dao->insert();
522 }
523
524 /**
525 * Given an associative array of name/value pairs, extract all the values
526 * that belong to this object and initialize the object with said values
527 *
6a0b768e
TO
528 * @param array $params
529 * (reference ) associative array of name/value pairs.
6a488035 530 *
795492f3
TO
531 * @return bool
532 * Did we copy all null values into the object
6a488035 533 */
00be9182 534 public function copyValues(&$params) {
6a488035
TO
535 $fields = &$this->fields();
536 $allNull = TRUE;
537 foreach ($fields as $name => $value) {
538 $dbName = $value['name'];
539 if (array_key_exists($dbName, $params)) {
540 $pValue = $params[$dbName];
541 $exists = TRUE;
542 }
543 elseif (array_key_exists($name, $params)) {
544 $pValue = $params[$name];
545 $exists = TRUE;
546 }
547 else {
548 $exists = FALSE;
549 }
550
551 // if there is no value then make the variable NULL
552 if ($exists) {
553 if ($pValue === '') {
554 $this->$dbName = 'null';
555 }
556 else {
557 $this->$dbName = $pValue;
558 $allNull = FALSE;
559 }
560 }
561 }
562 return $allNull;
563 }
564
565 /**
566 * Store all the values from this object in an associative array
567 * this is a destructive store, calling function is responsible
568 * for keeping sanity of id's.
569 *
6a0b768e
TO
570 * @param object $object
571 * The object that we are extracting data from.
572 * @param array $values
573 * (reference ) associative array of name/value pairs.
6a488035 574 */
00be9182 575 public static function storeValues(&$object, &$values) {
6a488035
TO
576 $fields = &$object->fields();
577 foreach ($fields as $name => $value) {
578 $dbName = $value['name'];
579 if (isset($object->$dbName) && $object->$dbName !== 'null') {
580 $values[$dbName] = $object->$dbName;
581 if ($name != $dbName) {
582 $values[$name] = $object->$dbName;
583 }
584 }
585 }
586 }
587
588 /**
100fef9d 589 * Create an attribute for this specific field. We only do this for strings and text
6a488035 590 *
6a0b768e
TO
591 * @param array $field
592 * The field under task.
6a488035 593 *
72b3a70c
CW
594 * @return array|null
595 * the attributes for the object
6a488035 596 */
00be9182 597 public static function makeAttribute($field) {
6a488035
TO
598 if ($field) {
599 if (CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_STRING) {
600 $maxLength = CRM_Utils_Array::value('maxlength', $field);
601 $size = CRM_Utils_Array::value('size', $field);
602 if ($maxLength || $size) {
603 $attributes = array();
604 if ($maxLength) {
605 $attributes['maxlength'] = $maxLength;
606 }
607 if ($size) {
608 $attributes['size'] = $size;
609 }
610 return $attributes;
611 }
612 }
613 elseif (CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_TEXT) {
614 $rows = CRM_Utils_Array::value('rows', $field);
615 if (!isset($rows)) {
616 $rows = 2;
617 }
618 $cols = CRM_Utils_Array::value('cols', $field);
619 if (!isset($cols)) {
620 $cols = 80;
621 }
622
353ffa53 623 $attributes = array();
6a488035
TO
624 $attributes['rows'] = $rows;
625 $attributes['cols'] = $cols;
626 return $attributes;
627 }
628 elseif (CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_INT || CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_FLOAT || CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_MONEY) {
629 $attributes['size'] = 6;
630 $attributes['maxlength'] = 14;
631 return $attributes;
632 }
633 }
634 return NULL;
635 }
636
637 /**
d09edf64 638 * Get the size and maxLength attributes for this text field.
6a488035
TO
639 * (or for all text fields) in the DAO object.
640 *
6a0b768e
TO
641 * @param string $class
642 * Name of DAO class.
643 * @param string $fieldName
644 * Field that i'm interested in or null if.
6a488035
TO
645 * you want the attributes for all DAO text fields
646 *
a6c01b45
CW
647 * @return array
648 * assoc array of name => attribute pairs
6a488035 649 */
00be9182 650 public static function getAttribute($class, $fieldName = NULL) {
353ffa53 651 $object = new $class();
6a488035
TO
652 $fields = &$object->fields();
653 if ($fieldName != NULL) {
654 $field = CRM_Utils_Array::value($fieldName, $fields);
655 return self::makeAttribute($field);
656 }
657 else {
658 $attributes = array();
659 foreach ($fields as $name => $field) {
660 $attribute = self::makeAttribute($field);
661 if ($attribute) {
662 $attributes[$name] = $attribute;
663 }
664 }
665
666 if (!empty($attributes)) {
667 return $attributes;
668 }
669 }
670 return NULL;
671 }
672
a0ee3941
EM
673 /**
674 * @param $type
675 *
676 * @throws Exception
677 */
00be9182 678 public static function transaction($type) {
6a488035
TO
679 CRM_Core_Error::fatal('This function is obsolete, please use CRM_Core_Transaction');
680 }
681
682 /**
fe482240 683 * Check if there is a record with the same name in the db.
6a488035 684 *
6a0b768e
TO
685 * @param string $value
686 * The value of the field we are checking.
687 * @param string $daoName
688 * The dao object name.
689 * @param string $daoID
690 * The id of the object being updated. u can change your name.
6a488035 691 * as long as there is no conflict
6a0b768e
TO
692 * @param string $fieldName
693 * The name of the field in the DAO.
6a488035 694 *
795492f3 695 * @return bool
a6c01b45 696 * true if object exists
6a488035 697 */
00be9182 698 public static function objectExists($value, $daoName, $daoID, $fieldName = 'name') {
353ffa53 699 $object = new $daoName();
6a488035
TO
700 $object->$fieldName = $value;
701
702 $config = CRM_Core_Config::singleton();
703
704 if ($object->find(TRUE)) {
705 return ($daoID && $object->id == $daoID) ? TRUE : FALSE;
706 }
707 else {
708 return TRUE;
709 }
710 }
711
712 /**
fe482240 713 * Check if there is a given column in a specific table.
6a488035
TO
714 *
715 * @param string $tableName
716 * @param string $columnName
6a0b768e
TO
717 * @param bool $i18nRewrite
718 * Whether to rewrite the query on multilingual setups.
6a488035 719 *
795492f3 720 * @return bool
a6c01b45 721 * true if exists, else false
6a488035 722 */
00be9182 723 public static function checkFieldExists($tableName, $columnName, $i18nRewrite = TRUE) {
6a488035
TO
724 $query = "
725SHOW COLUMNS
726FROM $tableName
727LIKE %1
728";
729 $params = array(1 => array($columnName, 'String'));
353ffa53 730 $dao = CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, $i18nRewrite);
6a488035
TO
731 $result = $dao->fetch() ? TRUE : FALSE;
732 $dao->free();
733 return $result;
734 }
735
736 /**
737 * Returns the storage engine used by given table-name(optional).
738 * Otherwise scans all the tables and return an array of all the
739 * distinct storage engines being used.
740 *
741 * @param string $tableName
742 *
2a6da8d7
EM
743 * @param int $maxTablesToCheck
744 * @param string $fieldName
745 *
6a488035 746 * @return array
6a488035 747 */
00be9182 748 public static function getStorageValues($tableName = NULL, $maxTablesToCheck = 10, $fieldName = 'Engine') {
6a488035
TO
749 $values = array();
750 $query = "SHOW TABLE STATUS LIKE %1";
751
752 $params = array();
753
754 if (isset($tableName)) {
755 $params = array(1 => array($tableName, 'String'));
756 }
757 else {
758 $params = array(1 => array('civicrm_%', 'String'));
759 }
760
761 $dao = CRM_Core_DAO::executeQuery($query, $params);
762
763 $count = 0;
764 while ($dao->fetch()) {
765 if (isset($values[$dao->$fieldName]) ||
766 // ignore import and other temp tables
767 strpos($dao->Name, 'civicrm_import_job_') !== FALSE ||
768 strpos($dao->Name, '_temp') !== FALSE
769 ) {
770 continue;
771 }
772 $values[$dao->$fieldName] = 1;
773 $count++;
774 if ($maxTablesToCheck &&
775 $count >= $maxTablesToCheck
776 ) {
777 break;
778 }
779 }
780 $dao->free();
781 return $values;
782 }
783
a0ee3941
EM
784 /**
785 * @param int $maxTablesToCheck
786 *
787 * @return bool
788 */
00be9182 789 public static function isDBMyISAM($maxTablesToCheck = 10) {
6a488035
TO
790 // show error if any of the tables, use 'MyISAM' storage engine.
791 $engines = self::getStorageValues(NULL, $maxTablesToCheck);
792 if (array_key_exists('MyISAM', $engines)) {
793 return TRUE;
794 }
795 return FALSE;
796 }
797
798 /**
799 * Checks if a constraint exists for a specified table.
800 *
801 * @param string $tableName
802 * @param string $constraint
803 *
795492f3 804 * @return bool
a6c01b45 805 * true if constraint exists, false otherwise
6a488035 806 */
00be9182 807 public static function checkConstraintExists($tableName, $constraint) {
6a488035
TO
808 static $show = array();
809
810 if (!array_key_exists($tableName, $show)) {
811 $query = "SHOW CREATE TABLE $tableName";
812 $dao = CRM_Core_DAO::executeQuery($query);
813
814 if (!$dao->fetch()) {
815 CRM_Core_Error::fatal();
816 }
817
818 $dao->free();
819 $show[$tableName] = $dao->Create_Table;
820 }
821
822 return preg_match("/\b$constraint\b/i", $show[$tableName]) ? TRUE : FALSE;
823 }
824
825 /**
826 * Checks if CONSTRAINT keyword exists for a specified table.
827 *
2a6da8d7
EM
828 * @param array $tables
829 *
830 * @throws Exception
6a488035 831 *
795492f3 832 * @return bool
a6c01b45 833 * true if CONSTRAINT keyword exists, false otherwise
6a488035 834 */
00be9182 835 public static function schemaRequiresRebuilding($tables = array("civicrm_contact")) {
6a488035 836 $show = array();
9b873358 837 foreach ($tables as $tableName) {
6a488035
TO
838 if (!array_key_exists($tableName, $show)) {
839 $query = "SHOW CREATE TABLE $tableName";
840 $dao = CRM_Core_DAO::executeQuery($query);
841
842 if (!$dao->fetch()) {
843 CRM_Core_Error::fatal();
844 }
845
846 $dao->free();
847 $show[$tableName] = $dao->Create_Table;
848 }
849
850 $result = preg_match("/\bCONSTRAINT\b\s/i", $show[$tableName]) ? TRUE : FALSE;
9b873358 851 if ($result == TRUE) {
6a488035
TO
852 continue;
853 }
c490a46a 854 else {
6a488035
TO
855 return FALSE;
856 }
857 }
858 return TRUE;
859 }
860
861 /**
862 * Checks if the FK constraint name is in the format 'FK_tableName_columnName'
863 * for a specified column of a table.
864 *
865 * @param string $tableName
866 * @param string $columnName
867 *
795492f3 868 * @return bool
a6c01b45 869 * true if in format, false otherwise
6a488035 870 */
00be9182 871 public static function checkFKConstraintInFormat($tableName, $columnName) {
6a488035
TO
872 static $show = array();
873
874 if (!array_key_exists($tableName, $show)) {
875 $query = "SHOW CREATE TABLE $tableName";
876 $dao = CRM_Core_DAO::executeQuery($query);
877
878 if (!$dao->fetch()) {
879 CRM_Core_Error::fatal();
880 }
881
882 $dao->free();
883 $show[$tableName] = $dao->Create_Table;
884 }
885 $constraint = "`FK_{$tableName}_{$columnName}`";
886 $pattern = "/\bCONSTRAINT\b\s+%s\s+\bFOREIGN\s+KEY\b\s/i";
353ffa53 887 return preg_match(sprintf($pattern, $constraint), $show[$tableName]) ? TRUE : FALSE;
6a488035
TO
888 }
889
890 /**
fe482240 891 * Check whether a specific column in a specific table has always the same value.
6a488035
TO
892 *
893 * @param string $tableName
894 * @param string $columnName
895 * @param string $columnValue
896 *
795492f3 897 * @return bool
a6c01b45 898 * true if the value is always $columnValue, false otherwise
6a488035 899 */
00be9182 900 public static function checkFieldHasAlwaysValue($tableName, $columnName, $columnValue) {
353ffa53
TO
901 $query = "SELECT * FROM $tableName WHERE $columnName != '$columnValue'";
902 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
903 $result = $dao->fetch() ? FALSE : TRUE;
904 $dao->free();
905 return $result;
906 }
907
908 /**
fe482240 909 * Check whether a specific column in a specific table is always NULL.
6a488035
TO
910 *
911 * @param string $tableName
912 * @param string $columnName
913 *
795492f3 914 * @return bool
a6c01b45 915 * true if if the value is always NULL, false otherwise
6a488035 916 */
00be9182 917 public static function checkFieldIsAlwaysNull($tableName, $columnName) {
353ffa53
TO
918 $query = "SELECT * FROM $tableName WHERE $columnName IS NOT NULL";
919 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
920 $result = $dao->fetch() ? FALSE : TRUE;
921 $dao->free();
922 return $result;
923 }
924
925 /**
fe482240 926 * Check if there is a given table in the database.
6a488035
TO
927 *
928 * @param string $tableName
929 *
795492f3 930 * @return bool
a6c01b45 931 * true if exists, else false
6a488035 932 */
00be9182 933 public static function checkTableExists($tableName) {
6a488035
TO
934 $query = "
935SHOW TABLES
936LIKE %1
937";
938 $params = array(1 => array($tableName, 'String'));
939
940 $dao = CRM_Core_DAO::executeQuery($query, $params);
941 $result = $dao->fetch() ? TRUE : FALSE;
942 $dao->free();
943 return $result;
944 }
945
a0ee3941
EM
946 /**
947 * @param $version
948 *
949 * @return bool
950 */
00be9182 951 public function checkVersion($version) {
6a488035
TO
952 $query = "
953SELECT version
954FROM civicrm_domain
955";
956 $dbVersion = CRM_Core_DAO::singleValueQuery($query);
957 return trim($version) == trim($dbVersion) ? TRUE : FALSE;
958 }
959
47ff2df7
AN
960 /**
961 * Find a DAO object for the given ID and return it.
962 *
6a0b768e
TO
963 * @param int $id
964 * Id of the DAO object being searched for.
47ff2df7 965 *
a6c01b45
CW
966 * @return object
967 * Object of the type of the class that called this function.
47ff2df7 968 */
00be9182 969 public static function findById($id) {
47ff2df7
AN
970 $object = new static();
971 $object->id = $id;
972 if (!$object->find(TRUE)) {
973 throw new Exception("Unable to find a " . get_called_class() . " with id {$id}.");
974 }
975 return $object;
976 }
977
63782ba4
TO
978 /**
979 * Returns all results as array-encoded records.
980 *
981 * @return array
982 */
983 public function fetchAll() {
984 $result = array();
985 while ($this->fetch()) {
986 $result[] = $this->toArray();
987 }
988 return $result;
989 }
990
6a488035
TO
991 /**
992 * Given a DAO name, a column name and a column value, find the record and GET the value of another column in that record
993 *
6a0b768e
TO
994 * @param string $daoName
995 * Name of the DAO (Example: CRM_Contact_DAO_Contact to retrieve value from a contact).
996 * @param int $searchValue
997 * Value of the column you want to search by.
998 * @param string $returnColumn
999 * Name of the column you want to GET the value of.
1000 * @param string $searchColumn
1001 * Name of the column you want to search by.
1002 * @param bool $force
1003 * Skip use of the cache.
6a488035 1004 *
72b3a70c
CW
1005 * @return string|null
1006 * Value of $returnColumn in the retrieved record
6a488035 1007 */
00be9182 1008 public static function getFieldValue($daoName, $searchValue, $returnColumn = 'name', $searchColumn = 'id', $force = FALSE) {
6a488035
TO
1009 if (
1010 empty($searchValue) ||
1011 trim(strtolower($searchValue)) == 'null'
1012 ) {
1013 // adding this year since developers forget to check for an id
1014 // or for the 'null' (which is a bad DAO kludge)
1015 // and hence we get the first value in the db
1016 CRM_Core_Error::fatal();
1017 }
1018
1019 $cacheKey = "{$daoName}:{$searchValue}:{$returnColumn}:{$searchColumn}";
1020 if (self::$_dbColumnValueCache === NULL) {
1021 self::$_dbColumnValueCache = array();
1022 }
1023
1024 if (!array_key_exists($cacheKey, self::$_dbColumnValueCache) || $force) {
353ffa53 1025 $object = new $daoName();
6a488035
TO
1026 $object->$searchColumn = $searchValue;
1027 $object->selectAdd();
1028 $object->selectAdd($returnColumn);
1029
1030 $result = NULL;
1031 if ($object->find(TRUE)) {
1032 $result = $object->$returnColumn;
1033 }
1034 $object->free();
1035
1036 self::$_dbColumnValueCache[$cacheKey] = $result;
1037 }
1038 return self::$_dbColumnValueCache[$cacheKey];
1039 }
1040
1041 /**
1042 * Given a DAO name, a column name and a column value, find the record and SET the value of another column in that record
1043 *
6a0b768e
TO
1044 * @param string $daoName
1045 * Name of the DAO (Example: CRM_Contact_DAO_Contact to retrieve value from a contact).
1046 * @param int $searchValue
1047 * Value of the column you want to search by.
1048 * @param string $setColumn
1049 * Name of the column you want to SET the value of.
1050 * @param string $setValue
1051 * SET the setColumn to this value.
1052 * @param string $searchColumn
1053 * Name of the column you want to search by.
6a488035 1054 *
795492f3 1055 * @return bool
a6c01b45 1056 * true if we found and updated the object, else false
6a488035 1057 */
00be9182 1058 public static function setFieldValue($daoName, $searchValue, $setColumn, $setValue, $searchColumn = 'id') {
353ffa53 1059 $object = new $daoName();
6a488035
TO
1060 $object->selectAdd();
1061 $object->selectAdd("$searchColumn, $setColumn");
1062 $object->$searchColumn = $searchValue;
1063 $result = FALSE;
1064 if ($object->find(TRUE)) {
1065 $object->$setColumn = $setValue;
1066 if ($object->save()) {
1067 $result = TRUE;
1068 }
1069 }
1070 $object->free();
1071 return $result;
1072 }
1073
1074 /**
fe482240 1075 * Get sort string.
6a488035
TO
1076 *
1077 * @param array|object $sort either array or CRM_Utils_Sort
6a0b768e
TO
1078 * @param string $default
1079 * Default sort value.
6a488035 1080 *
a6c01b45
CW
1081 * @return string
1082 * sortString
6a488035 1083 */
00be9182 1084 public static function getSortString($sort, $default = NULL) {
6a488035
TO
1085 // check if sort is of type CRM_Utils_Sort
1086 if (is_a($sort, 'CRM_Utils_Sort')) {
1087 return $sort->orderBy();
1088 }
1089
1090 // is it an array specified as $field => $sortDirection ?
1091 if ($sort) {
1092 foreach ($sort as $k => $v) {
1093 $sortString .= "$k $v,";
1094 }
1095 return rtrim($sortString, ',');
1096 }
1097 return $default;
1098 }
1099
1100 /**
fe482240 1101 * Fetch object based on array of properties.
6a488035 1102 *
6a0b768e
TO
1103 * @param string $daoName
1104 * Name of the dao object.
1105 * @param array $params
1106 * (reference ) an assoc array of name/value pairs.
1107 * @param array $defaults
1108 * (reference ) an assoc array to hold the flattened values.
1109 * @param array $returnProperities
1110 * An assoc array of fields that need to be returned, eg array( 'first_name', 'last_name').
6a488035 1111 *
a6c01b45
CW
1112 * @return object
1113 * an object of type referenced by daoName
6a488035 1114 */
00be9182 1115 public static function commonRetrieve($daoName, &$params, &$defaults, $returnProperities = NULL) {
353ffa53 1116 $object = new $daoName();
6a488035
TO
1117 $object->copyValues($params);
1118
1119 // return only specific fields if returnproperties are sent
1120 if (!empty($returnProperities)) {
1121 $object->selectAdd();
1122 $object->selectAdd(implode(',', $returnProperities));
1123 }
1124
1125 if ($object->find(TRUE)) {
1126 self::storeValues($object, $defaults);
1127 return $object;
1128 }
1129 return NULL;
1130 }
1131
1132 /**
fe482240 1133 * Delete the object records that are associated with this contact.
6a488035 1134 *
6a0b768e
TO
1135 * @param string $daoName
1136 * Name of the dao object.
1137 * @param int $contactId
1138 * Id of the contact to delete.
6a488035 1139 */
00be9182 1140 public static function deleteEntityContact($daoName, $contactId) {
353ffa53 1141 $object = new $daoName();
6a488035
TO
1142
1143 $object->entity_table = 'civicrm_contact';
1144 $object->entity_id = $contactId;
1145 $object->delete();
1146 }
1147
1148 /**
fe482240 1149 * Execute a query.
6a488035 1150 *
6a0b768e
TO
1151 * @param string $query
1152 * Query to be executed.
6a488035 1153 *
2a6da8d7
EM
1154 * @param array $params
1155 * @param bool $abort
1156 * @param null $daoName
1157 * @param bool $freeDAO
1158 * @param bool $i18nRewrite
1159 * @param bool $trapException
1160 *
5f1ebaec 1161 * @return CRM_Core_DAO|object
16b10e64 1162 * object that holds the results of the query
5f1ebaec
EM
1163 * NB - if this is defined as just returning a DAO phpstorm keeps pointing
1164 * out all the properties that are not part of the DAO
6a488035 1165 */
795492f3 1166 public static function &executeQuery(
6a488035 1167 $query,
353ffa53
TO
1168 $params = array(),
1169 $abort = TRUE,
1170 $daoName = NULL,
1171 $freeDAO = FALSE,
1172 $i18nRewrite = TRUE,
6a488035
TO
1173 $trapException = FALSE
1174 ) {
1175 $queryStr = self::composeQuery($query, $params, $abort);
6a488035
TO
1176
1177 if (!$daoName) {
1178 $dao = new CRM_Core_DAO();
1179 }
1180 else {
353ffa53 1181 $dao = new $daoName();
6a488035
TO
1182 }
1183
1184 if ($trapException) {
6a4257d4 1185 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
6a488035
TO
1186 }
1187
1188 $result = $dao->query($queryStr, $i18nRewrite);
1189
6a488035
TO
1190 if (is_a($result, 'DB_Error')) {
1191 return $result;
1192 }
1193
1194 if ($freeDAO ||
1195 preg_match('/^(insert|update|delete|create|drop|replace)/i', $queryStr)
1196 ) {
b05b5a19 1197 // we typically do this for insert/update/delete statements OR if explicitly asked to
6a488035
TO
1198 // free the dao
1199 $dao->free();
1200 }
1201 return $dao;
1202 }
1203
1204 /**
fe482240 1205 * Execute a query and get the single result.
6a488035 1206 *
6a0b768e
TO
1207 * @param string $query
1208 * Query to be executed.
e869b07d
CW
1209 * @param array $params
1210 * @param bool $abort
1211 * @param bool $i18nRewrite
72b3a70c
CW
1212 * @return string|null
1213 * the result of the query if any
6a488035 1214 *
6a488035 1215 */
795492f3 1216 public static function &singleValueQuery(
f9f40af3 1217 $query,
353ffa53
TO
1218 $params = array(),
1219 $abort = TRUE,
6a488035
TO
1220 $i18nRewrite = TRUE
1221 ) {
1222 $queryStr = self::composeQuery($query, $params, $abort);
1223
1224 static $_dao = NULL;
1225
1226 if (!$_dao) {
1227 $_dao = new CRM_Core_DAO();
1228 }
1229
1230 $_dao->query($queryStr, $i18nRewrite);
1231
1232 $result = $_dao->getDatabaseResult();
1233 $ret = NULL;
1234 if ($result) {
1235 $row = $result->fetchRow();
1236 if ($row) {
1237 $ret = $row[0];
1238 }
1239 }
1240 $_dao->free();
1241 return $ret;
1242 }
1243
a0ee3941
EM
1244 /**
1245 * @param $query
c490a46a 1246 * @param array $params
a0ee3941
EM
1247 * @param bool $abort
1248 *
1249 * @return string
1250 * @throws Exception
1251 */
00be9182 1252 public static function composeQuery($query, &$params, $abort = TRUE) {
6a488035
TO
1253 $tr = array();
1254 foreach ($params as $key => $item) {
1255 if (is_numeric($key)) {
1256 if (CRM_Utils_Type::validate($item[0], $item[1]) !== NULL) {
1257 $item[0] = self::escapeString($item[0]);
1258 if ($item[1] == 'String' ||
1259 $item[1] == 'Memo' ||
1260 $item[1] == 'Link'
1261 ) {
887a4028
A
1262 // Support class constants stipulating wildcard characters and/or
1263 // non-quoting of strings. Also support legacy code which may be
1264 // passing in TRUE or 1 for $item[2], which used to indicate the
1265 // use of wildcard characters.
1266 if (!empty($item[2])) {
1267 if ($item[2] & CRM_Core_DAO::QUERY_FORMAT_WILDCARD || $item[2] === TRUE) {
1268 $item[0] = "'%{$item[0]}%'";
1269 }
1270 elseif (!($item[2] & CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES)) {
1271 $item[0] = "'{$item[0]}'";
1272 }
6a488035
TO
1273 }
1274 else {
1275 $item[0] = "'{$item[0]}'";
1276 }
1277 }
1278
1279 if (($item[1] == 'Date' || $item[1] == 'Timestamp') &&
1280 strlen($item[0]) == 0
1281 ) {
1282 $item[0] = 'null';
1283 }
1284
1285 $tr['%' . $key] = $item[0];
1286 }
1287 elseif ($abort) {
1288 CRM_Core_Error::fatal("{$item[0]} is not of type {$item[1]}");
1289 }
1290 }
1291 }
1292
e2508c90 1293 return strtr($query, $tr);
6a488035
TO
1294 }
1295
a0ee3941
EM
1296 /**
1297 * @param null $ids
1298 */
00be9182 1299 public static function freeResult($ids = NULL) {
6a488035
TO
1300 global $_DB_DATAOBJECT;
1301
6a488035
TO
1302 if (!$ids) {
1303 if (!$_DB_DATAOBJECT ||
1304 !isset($_DB_DATAOBJECT['RESULTS'])
1305 ) {
1306 return;
1307 }
1308 $ids = array_keys($_DB_DATAOBJECT['RESULTS']);
1309 }
1310
1311 foreach ($ids as $id) {
1312 if (isset($_DB_DATAOBJECT['RESULTS'][$id])) {
1313 if (is_resource($_DB_DATAOBJECT['RESULTS'][$id]->result)) {
1314 mysql_free_result($_DB_DATAOBJECT['RESULTS'][$id]->result);
1315 }
1316 unset($_DB_DATAOBJECT['RESULTS'][$id]);
1317 }
1318
1319 if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$id])) {
1320 unset($_DB_DATAOBJECT['RESULTFIELDS'][$id]);
1321 }
1322 }
1323 }
1324
1325 /**
d09edf64 1326 * make a shallow copy of an object.
6a488035
TO
1327 * and all the fields in the object
1328 *
6a0b768e
TO
1329 * @param string $daoName
1330 * Name of the dao.
1331 * @param array $criteria
1332 * Array of all the fields & values.
6a488035 1333 * on which basis to copy
6a0b768e
TO
1334 * @param array $newData
1335 * Array of all the fields & values.
6a488035 1336 * to be copied besides the other fields
6a0b768e
TO
1337 * @param string $fieldsFix
1338 * Array of fields that you want to prefix/suffix/replace.
1339 * @param string $blockCopyOfDependencies
1340 * Fields that you want to block from.
6a488035
TO
1341 * getting copied
1342 *
1343 *
72b3a70c
CW
1344 * @return CRM_Core_DAO
1345 * the newly created copy of the object
6a488035 1346 */
795492f3 1347 public static function &copyGeneric($daoName, $criteria, $newData = NULL, $fieldsFix = NULL, $blockCopyOfDependencies = NULL) {
353ffa53 1348 $object = new $daoName();
6a488035
TO
1349 if (!$newData) {
1350 $object->id = $criteria['id'];
1351 }
1352 else {
1353 foreach ($criteria as $key => $value) {
1354 $object->$key = $value;
1355 }
1356 }
1357
1358 $object->find();
1359 while ($object->fetch()) {
1360
1361 // all the objects except with $blockCopyOfDependencies set
1362 // be copied - addresses #CRM-1962
1363
1364 if ($blockCopyOfDependencies && $object->$blockCopyOfDependencies) {
1365 break;
1366 }
1367
353ffa53 1368 $newObject = new $daoName();
6a488035
TO
1369
1370 $fields = &$object->fields();
1371 if (!is_array($fieldsFix)) {
353ffa53
TO
1372 $fieldsToPrefix = array();
1373 $fieldsToSuffix = array();
6a488035
TO
1374 $fieldsToReplace = array();
1375 }
a7488080 1376 if (!empty($fieldsFix['prefix'])) {
6a488035
TO
1377 $fieldsToPrefix = $fieldsFix['prefix'];
1378 }
a7488080 1379 if (!empty($fieldsFix['suffix'])) {
6a488035
TO
1380 $fieldsToSuffix = $fieldsFix['suffix'];
1381 }
a7488080 1382 if (!empty($fieldsFix['replace'])) {
6a488035
TO
1383 $fieldsToReplace = $fieldsFix['replace'];
1384 }
1385
1386 foreach ($fields as $name => $value) {
1387 if ($name == 'id' || $value['name'] == 'id') {
1388 // copy everything but the id!
1389 continue;
1390 }
1391
1392 $dbName = $value['name'];
a1305c92 1393 $type = CRM_Utils_Type::typeToString($value['type']);
6a488035
TO
1394 $newObject->$dbName = $object->$dbName;
1395 if (isset($fieldsToPrefix[$dbName])) {
1396 $newObject->$dbName = $fieldsToPrefix[$dbName] . $newObject->$dbName;
1397 }
1398 if (isset($fieldsToSuffix[$dbName])) {
1399 $newObject->$dbName .= $fieldsToSuffix[$dbName];
1400 }
1401 if (isset($fieldsToReplace[$dbName])) {
1402 $newObject->$dbName = $fieldsToReplace[$dbName];
1403 }
1404
6c71f6c7 1405 if ($type == 'Timestamp' || $type == 'Date') {
6a488035
TO
1406 $newObject->$dbName = CRM_Utils_Date::isoToMysql($newObject->$dbName);
1407 }
1408
1409 if ($newData) {
1410 foreach ($newData as $k => $v) {
1411 $newObject->$k = $v;
1412 }
1413 }
1414 }
1415 $newObject->save();
1416 }
1417 return $newObject;
1418 }
1419
7a9ab499
EM
1420 /**
1421 * Cascade update through related entities.
1422 *
1423 * @param string $daoName
1424 * @param $fromId
1425 * @param $toId
1426 * @param array $newData
1427 *
1428 * @return null
1429 */
00be9182 1430 public static function cascadeUpdate($daoName, $fromId, $toId, $newData = array()) {
353ffa53 1431 $object = new $daoName();
62933949 1432 $object->id = $fromId;
1433
1434 if ($object->find(TRUE)) {
353ffa53 1435 $newObject = new $daoName();
62933949 1436 $newObject->id = $toId;
1437
1438 if ($newObject->find(TRUE)) {
1439 $fields = &$object->fields();
1440 foreach ($fields as $name => $value) {
1441 if ($name == 'id' || $value['name'] == 'id') {
1442 // copy everything but the id!
1443 continue;
1444 }
1445
1446 $colName = $value['name'];
1447 $newObject->$colName = $object->$colName;
1448
1449 if (substr($name, -5) == '_date' ||
1450 substr($name, -10) == '_date_time'
1451 ) {
1452 $newObject->$colName = CRM_Utils_Date::isoToMysql($newObject->$colName);
1453 }
1454 }
1455 foreach ($newData as $k => $v) {
1456 $newObject->$k = $v;
1457 }
1458 $newObject->save();
1459 return $newObject;
1460 }
1461 }
1462 return CRM_Core_DAO::$_nullObject;
1463 }
1464
6a488035
TO
1465 /**
1466 * Given the component id, compute the contact id
1467 * since its used for things like send email
b3342109
EM
1468 *
1469 * @param $componentIDs
100fef9d 1470 * @param string $tableName
b3342109
EM
1471 *
1472 * @return array
6a488035 1473 */
a5611c8e 1474 public static function &getContactIDsFromComponent(&$componentIDs, $tableName) {
6a488035
TO
1475 $contactIDs = array();
1476
1477 if (empty($componentIDs)) {
1478 return $contactIDs;
1479 }
1480
1481 $IDs = implode(',', $componentIDs);
1482 $query = "
1483SELECT contact_id
1484 FROM $tableName
1485 WHERE id IN ( $IDs )
1486";
1487
1488 $dao = CRM_Core_DAO::executeQuery($query);
1489 while ($dao->fetch()) {
1490 $contactIDs[] = $dao->contact_id;
1491 }
1492 return $contactIDs;
1493 }
1494
1495 /**
fe482240 1496 * Fetch object based on array of properties.
6a488035 1497 *
6a0b768e
TO
1498 * @param string $daoName
1499 * Name of the dao object.
dd244018 1500 * @param string $fieldIdName
100fef9d 1501 * @param int $fieldId
dd244018 1502 * @param $details
6a0b768e
TO
1503 * @param array $returnProperities
1504 * An assoc array of fields that need to be returned, eg array( 'first_name', 'last_name').
dd244018 1505 *
a6c01b45
CW
1506 * @return object
1507 * an object of type referenced by daoName
6a488035 1508 */
00be9182 1509 public static function commonRetrieveAll($daoName, $fieldIdName = 'id', $fieldId, &$details, $returnProperities = NULL) {
795492f3 1510 require_once str_replace('_', DIRECTORY_SEPARATOR, $daoName) . ".php";
353ffa53 1511 $object = new $daoName();
6a488035
TO
1512 $object->$fieldIdName = $fieldId;
1513
1514 // return only specific fields if returnproperties are sent
1515 if (!empty($returnProperities)) {
1516 $object->selectAdd();
1517 $object->selectAdd('id');
1518 $object->selectAdd(implode(',', $returnProperities));
1519 }
1520
1521 $object->find();
1522 while ($object->fetch()) {
1523 $defaults = array();
1524 self::storeValues($object, $defaults);
1525 $details[$object->id] = $defaults;
1526 }
1527
1528 return $details;
1529 }
1530
00be9182 1531 public static function dropAllTables() {
6a488035
TO
1532
1533 // first drop all the custom tables we've created
1534 CRM_Core_BAO_CustomGroup::dropAllTables();
1535
1536 // drop all multilingual views
1537 CRM_Core_I18n_Schema::dropAllViews();
1538
1539 CRM_Utils_File::sourceSQLFile(CIVICRM_DSN,
1540 dirname(__FILE__) . DIRECTORY_SEPARATOR .
1541 '..' . DIRECTORY_SEPARATOR .
1542 '..' . DIRECTORY_SEPARATOR .
1543 'sql' . DIRECTORY_SEPARATOR .
1544 'civicrm_drop.mysql'
1545 );
1546 }
1547
a0ee3941
EM
1548 /**
1549 * @param $string
1550 *
1551 * @return string
1552 */
00be9182 1553 public static function escapeString($string) {
6a488035
TO
1554 static $_dao = NULL;
1555
1556 if (!$_dao) {
74032946
TO
1557 // If this is an atypical case (e.g. preparing .sql files
1558 // before Civi has been installed), then we fallback to
1559 // DB-less escaping helper (mysql_real_escape_string).
1560 // Note: In typical usage, escapeString() will only
1561 // check one conditional ("if !$_dao") rather than
1562 // two conditionals ("if !defined(DSN)")
1563 if (!defined('CIVICRM_DSN')) {
1564 if (function_exists('mysql_real_escape_string')) {
1565 return mysql_real_escape_string($string);
0db6c3e1
TO
1566 }
1567 else {
74032946
TO
1568 throw new CRM_Core_Exception("Cannot generate SQL. \"mysql_real_escape_string\" is missing. Have you installed PHP \"mysql\" extension?");
1569 }
1570 }
1571
6a488035
TO
1572 $_dao = new CRM_Core_DAO();
1573 }
1574
1575 return $_dao->escape($string);
1576 }
1577
1578 /**
1579 * Escape a list of strings for use with "WHERE X IN (...)" queries.
1580 *
5a4f6742
CW
1581 * @param array $strings
1582 * @param string $default
1583 * the value to use if $strings has no elements.
a6c01b45
CW
1584 * @return string
1585 * eg "abc","def","ghi"
6a488035 1586 */
00be9182 1587 public static function escapeStrings($strings, $default = NULL) {
6a488035
TO
1588 static $_dao = NULL;
1589 if (!$_dao) {
1590 $_dao = new CRM_Core_DAO();
1591 }
1592
1593 if (empty($strings)) {
1594 return $default;
1595 }
1596
1597 $escapes = array_map(array($_dao, 'escape'), $strings);
1598 return '"' . implode('","', $escapes) . '"';
1599 }
1600
a0ee3941
EM
1601 /**
1602 * @param $string
1603 *
1604 * @return string
1605 */
00be9182 1606 public static function escapeWildCardString($string) {
6a488035
TO
1607 // CRM-9155
1608 // ensure we escape the single characters % and _ which are mysql wild
1609 // card characters and could come in via sortByCharacter
1610 // note that mysql does not escape these characters
1611 if ($string && in_array($string,
1612 array('%', '_', '%%', '_%')
353ffa53
TO
1613 )
1614 ) {
6a488035
TO
1615 return '\\' . $string;
1616 }
1617
1618 return self::escapeString($string);
1619 }
1620
92b83508
EM
1621 /**
1622 * Creates a test object, including any required objects it needs via recursion
b3342109
EM
1623 * createOnly: only create in database, do not store or return the objects (useful for perf testing)
1624 * ONLY USE FOR TESTING
1625 *
c490a46a 1626 * @param string $daoName
b3342109
EM
1627 * @param array $params
1628 * @param int $numObjects
1629 * @param bool $createOnly
1630 *
795492f3
TO
1631 * @return object|array|NULL
1632 * NULL if $createOnly. A single object if $numObjects==1. Otherwise, an array of multiple objects.
92b83508 1633 */
795492f3 1634 public static function createTestObject(
6a488035
TO
1635 $daoName,
1636 $params = array(),
1637 $numObjects = 1,
1638 $createOnly = FALSE
1639 ) {
b6262a4c
EM
1640 //this is a test function also backtrace is set for the test suite it sometimes unsets itself
1641 // so we re-set here in case
1642 $config = CRM_Core_Config::singleton();
1643 $config->backtrace = TRUE;
1644
6a488035
TO
1645 static $counter = 0;
1646 CRM_Core_DAO::$_testEntitiesToSkip = array(
1647 'CRM_Core_DAO_Worldregion',
1648 'CRM_Core_DAO_StateProvince',
1649 'CRM_Core_DAO_Country',
1650 'CRM_Core_DAO_Domain',
795492f3 1651 'CRM_Financial_DAO_FinancialType',
353ffa53 1652 //because valid ones exist & we use pick them due to pseudoconstant can't reliably create & delete these
6a488035
TO
1653 );
1654
2069d1b7
TO
1655 // Prefer to instantiate BAO's instead of DAO's (when possible)
1656 // so that assignTestValue()/assignTestFK() can be overloaded.
1657 $baoName = str_replace('_DAO_', '_BAO_', $daoName);
1658 if (class_exists($baoName)) {
1659 $daoName = $baoName;
1660 }
1661
6a488035
TO
1662 for ($i = 0; $i < $numObjects; ++$i) {
1663
1664 ++$counter;
e79cd558 1665 /** @var CRM_Core_DAO $object */
ab9aa379 1666 $object = new $daoName();
6a488035 1667
353ffa53 1668 $fields = &$object->fields();
e1b64aab
TO
1669 foreach ($fields as $fieldName => $fieldDef) {
1670 $dbName = $fieldDef['name'];
f290b6ef 1671 $FKClassName = CRM_Utils_Array::value('FKClassName', $fieldDef);
e1b64aab 1672 $required = CRM_Utils_Array::value('required', $fieldDef);
f290b6ef 1673
6a488035
TO
1674 if (CRM_Utils_Array::value($dbName, $params) !== NULL && !is_array($params[$dbName])) {
1675 $object->$dbName = $params[$dbName];
1676 }
1677
1678 elseif ($dbName != 'id') {
f290b6ef 1679 if ($FKClassName != NULL) {
e79cd558 1680 $object->assignTestFK($fieldName, $fieldDef, $params);
6a488035 1681 continue;
0db6c3e1
TO
1682 }
1683 else {
f290b6ef 1684 $object->assignTestValue($fieldName, $fieldDef, $counter);
6a488035 1685 }
6a488035
TO
1686 }
1687 }
b3342109 1688
6a488035
TO
1689 $object->save();
1690
1691 if (!$createOnly) {
6a488035 1692 $objects[$i] = $object;
6a488035 1693 }
f290b6ef
TO
1694 else {
1695 unset($object);
1696 }
6a488035
TO
1697 }
1698
1699 if ($createOnly) {
795492f3 1700 return NULL;
6a488035 1701 }
f290b6ef
TO
1702 elseif ($numObjects == 1) {
1703 return $objects[0];
1704 }
1705 else {
1706 return $objects;
1707 }
6a488035
TO
1708 }
1709
92b83508 1710 /**
fe482240 1711 * Deletes the this object plus any dependent objects that are associated with it.
92b83508 1712 * ONLY USE FOR TESTING
b3342109 1713 *
c490a46a 1714 * @param string $daoName
b3342109 1715 * @param array $params
92b83508 1716 */
00be9182 1717 public static function deleteTestObjects($daoName, $params = array()) {
b6262a4c
EM
1718 //this is a test function also backtrace is set for the test suite it sometimes unsets itself
1719 // so we re-set here in case
1720 $config = CRM_Core_Config::singleton();
1721 $config->backtrace = TRUE;
6a488035 1722
b6262a4c 1723 $object = new $daoName();
6a488035
TO
1724 $object->id = CRM_Utils_Array::value('id', $params);
1725
1726 $deletions = array(); // array(array(0 => $daoName, 1 => $daoParams))
1727 if ($object->find(TRUE)) {
1728
1729 $fields = &$object->fields();
1730 foreach ($fields as $name => $value) {
1731
1732 $dbName = $value['name'];
1733
1734 $FKClassName = CRM_Utils_Array::value('FKClassName', $value);
1735 $required = CRM_Utils_Array::value('required', $value);
1736 if ($FKClassName != NULL
1737 && $object->$dbName
1738 && !in_array($FKClassName, CRM_Core_DAO::$_testEntitiesToSkip)
806e9b71
EM
1739 && ($required || $dbName == 'contact_id')
1740 //I'm a bit stuck on this one - we might need to change the singleValueAlter so that the entities don't share a contact
1741 // to make this test process pass - line below makes pass for now
353ffa53
TO
1742 && $dbName != 'member_of_contact_id'
1743 ) {
6a488035
TO
1744 $deletions[] = array($FKClassName, array('id' => $object->$dbName)); // x
1745 }
1746 }
1747 }
1748
1749 $object->delete();
1750
1751 foreach ($deletions as $deletion) {
1752 CRM_Core_DAO::deleteTestObjects($deletion[0], $deletion[1]);
353ffa53 1753 }
6a488035
TO
1754 }
1755
64d24a64 1756 /**
fe482240 1757 * Set defaults when creating new entity.
64d24a64
EM
1758 * (don't call this set defaults as already in use with different signature in some places)
1759 *
c490a46a 1760 * @param array $params
64d24a64
EM
1761 * @param $defaults
1762 */
00be9182 1763 public static function setCreateDefaults(&$params, $defaults) {
64d24a64
EM
1764 if (isset($params['id'])) {
1765 return;
1766 }
1767 foreach ($defaults as $key => $value) {
1768 if (!array_key_exists($key, $params) || $params[$key] === NULL) {
1769 $params[$key] = $value;
1770 }
1771 }
1772 }
1773
a0ee3941
EM
1774 /**
1775 * @param string $prefix
1776 * @param bool $addRandomString
1777 * @param null $string
1778 *
1779 * @return string
1780 */
00be9182 1781 public static function createTempTableName($prefix = 'civicrm', $addRandomString = TRUE, $string = NULL) {
6a488035
TO
1782 $tableName = $prefix . "_temp";
1783
1784 if ($addRandomString) {
1785 if ($string) {
1786 $tableName .= "_" . $string;
1787 }
1788 else {
1789 $tableName .= "_" . md5(uniqid('', TRUE));
1790 }
1791 }
1792 return $tableName;
1793 }
1794
a0ee3941
EM
1795 /**
1796 * @param bool $view
1797 * @param bool $trigger
1798 *
1799 * @return bool
1800 */
00be9182 1801 public static function checkTriggerViewPermission($view = TRUE, $trigger = TRUE) {
6a488035
TO
1802 // test for create view and trigger permissions and if allowed, add the option to go multilingual
1803 // and logging
1804 // I'm not sure why we use the getStaticProperty for an error, rather than checking for DB_Error
6a4257d4 1805 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
6a488035
TO
1806 $dao = new CRM_Core_DAO();
1807 if ($view) {
1808 $dao->query('CREATE OR REPLACE VIEW civicrm_domain_view AS SELECT * FROM civicrm_domain');
1809 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
1810 return FALSE;
1811 }
1812 }
1813
1814 if ($trigger) {
1815 $result = $dao->query('CREATE TRIGGER civicrm_domain_trigger BEFORE INSERT ON civicrm_domain FOR EACH ROW BEGIN END');
1816 if (PEAR::getStaticProperty('DB_DataObject', 'lastError') || is_a($result, 'DB_Error')) {
6a488035
TO
1817 if ($view) {
1818 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1819 }
1820 return FALSE;
1821 }
1822
1823 $dao->query('DROP TRIGGER IF EXISTS civicrm_domain_trigger');
1824 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
1825 if ($view) {
1826 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1827 }
1828 return FALSE;
1829 }
1830 }
1831
1832 if ($view) {
1833 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1834 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
1835 return FALSE;
1836 }
1837 }
6a488035
TO
1838
1839 return TRUE;
1840 }
1841
a0ee3941
EM
1842 /**
1843 * @param null $message
1844 * @param bool $printDAO
1845 */
00be9182 1846 public static function debugPrint($message = NULL, $printDAO = TRUE) {
6a488035
TO
1847 CRM_Utils_System::xMemory("{$message}: ");
1848
1849 if ($printDAO) {
1850 global $_DB_DATAOBJECT;
1851 $q = array();
1852 foreach (array_keys($_DB_DATAOBJECT['RESULTS']) as $id) {
1853 $q[] = $_DB_DATAOBJECT['RESULTS'][$id]->query;
1854 }
1855 CRM_Core_Error::debug('_DB_DATAOBJECT', $q);
1856 }
1857 }
1858
77b97be7
EM
1859 /**
1860 * Build a list of triggers via hook and add them to (err, reconcile them
1861 * with) the database.
1862 *
5a4f6742
CW
1863 * @param string $tableName
1864 * the specific table requiring a rebuild; or NULL to rebuild all tables.
77b97be7
EM
1865 * @param bool $force
1866 *
1867 * @see CRM-9716
1868 */
00be9182 1869 public static function triggerRebuild($tableName = NULL, $force = FALSE) {
6a488035
TO
1870 $info = array();
1871
795492f3 1872 $logging = new CRM_Logging_Schema();
e53944ef 1873 $logging->triggerInfo($info, $tableName, $force);
6a488035
TO
1874
1875 CRM_Core_I18n_Schema::triggerInfo($info, $tableName);
1876 CRM_Contact_BAO_Contact::triggerInfo($info, $tableName);
1877
1878 CRM_Utils_Hook::triggerInfo($info, $tableName);
1879
1880 // drop all existing triggers on all tables
1881 $logging->dropTriggers($tableName);
1882
1883 // now create the set of new triggers
0590c631 1884 self::createTriggers($info, $tableName);
6a488035
TO
1885 }
1886
aca2de91
CW
1887 /**
1888 * Because sql functions are sometimes lost, esp during db migration, we check here to avoid numerous support requests
1889 * @see http://issues.civicrm.org/jira/browse/CRM-13822
1890 * TODO: Alternative solutions might be
1891 * * Stop using functions and find another way to strip numeric characters from phones
1892 * * Give better error messages (currently a missing fn fatals with "unknown error")
1893 */
00be9182 1894 public static function checkSqlFunctionsExist() {
aca2de91
CW
1895 if (!self::$_checkedSqlFunctionsExist) {
1896 self::$_checkedSqlFunctionsExist = TRUE;
1897 $dao = CRM_Core_DAO::executeQuery("SHOW function status WHERE db = database() AND name = 'civicrm_strip_non_numeric'");
1898 if (!$dao->fetch()) {
1899 self::triggerRebuild();
1900 }
1901 }
1902 }
1903
6a488035 1904 /**
fe482240 1905 * Wrapper function to drop triggers.
6a488035 1906 *
5a4f6742
CW
1907 * @param string $tableName
1908 * the specific table requiring a rebuild; or NULL to rebuild all tables.
6a488035 1909 */
00be9182 1910 public static function dropTriggers($tableName = NULL) {
6a488035
TO
1911 $info = array();
1912
795492f3 1913 $logging = new CRM_Logging_Schema();
6a488035
TO
1914 $logging->triggerInfo($info, $tableName);
1915
1916 // drop all existing triggers on all tables
1917 $logging->dropTriggers($tableName);
1918 }
1919
1920 /**
5a4f6742
CW
1921 * @param array $info
1922 * per hook_civicrm_triggerInfo.
1923 * @param string $onlyTableName
1924 * the specific table requiring a rebuild; or NULL to rebuild all tables.
6a488035 1925 */
00be9182 1926 public static function createTriggers(&$info, $onlyTableName = NULL) {
6a488035
TO
1927 // Validate info array, should probably raise errors?
1928 if (is_array($info) == FALSE) {
1929 return;
1930 }
1931
1932 $triggers = array();
1933
1934 // now enumerate the tables and the events and collect the same set in a different format
1935 foreach ($info as $value) {
1936
1937 // clean the incoming data, skip malformed entries
1938 // TODO: malformed entries should raise errors or get logged.
1939 if (isset($value['table']) == FALSE ||
1940 isset($value['event']) == FALSE ||
1941 isset($value['when']) == FALSE ||
1942 isset($value['sql']) == FALSE
1943 ) {
1944 continue;
1945 }
1946
1947 if (is_string($value['table']) == TRUE) {
1948 $tables = array($value['table']);
1949 }
1950 else {
1951 $tables = $value['table'];
1952 }
1953
1954 if (is_string($value['event']) == TRUE) {
1955 $events = array(strtolower($value['event']));
1956 }
1957 else {
1958 $events = array_map('strtolower', $value['event']);
1959 }
1960
1961 $whenName = strtolower($value['when']);
1962
1963 foreach ($tables as $tableName) {
1964 if (!isset($triggers[$tableName])) {
1965 $triggers[$tableName] = array();
1966 }
1967
1968 foreach ($events as $eventName) {
1969 $template_params = array('{tableName}', '{eventName}');
1970 $template_values = array($tableName, $eventName);
1971
1972 $sql = str_replace($template_params,
1973 $template_values,
1974 $value['sql']
1975 );
1976 $variables = str_replace($template_params,
1977 $template_values,
1978 CRM_Utils_Array::value('variables', $value)
1979 );
1980
1981 if (!isset($triggers[$tableName][$eventName])) {
1982 $triggers[$tableName][$eventName] = array();
1983 }
1984
1985 if (!isset($triggers[$tableName][$eventName][$whenName])) {
1986 // We're leaving out cursors, conditions, and handlers for now
1987 // they are kind of dangerous in this context anyway
1988 // better off putting them in stored procedures
1989 $triggers[$tableName][$eventName][$whenName] = array(
1990 'variables' => array(),
1991 'sql' => array(),
1992 );
1993 }
1994
1995 if ($variables) {
1996 $triggers[$tableName][$eventName][$whenName]['variables'][] = $variables;
1997 }
1998
1999 $triggers[$tableName][$eventName][$whenName]['sql'][] = $sql;
2000 }
2001 }
2002 }
2003
2004 // now spit out the sql
2005 foreach ($triggers as $tableName => $tables) {
2006 if ($onlyTableName != NULL && $onlyTableName != $tableName) {
2007 continue;
2008 }
2009 foreach ($tables as $eventName => $events) {
2010 foreach ($events as $whenName => $parts) {
353ffa53
TO
2011 $varString = implode("\n", $parts['variables']);
2012 $sqlString = implode("\n", $parts['sql']);
2013 $validName = CRM_Core_DAO::shortenSQLName($tableName, 48, TRUE);
6842bb53 2014 $triggerName = "{$validName}_{$whenName}_{$eventName}";
353ffa53 2015 $triggerSQL = "CREATE TRIGGER $triggerName $whenName $eventName ON $tableName FOR EACH ROW BEGIN $varString $sqlString END";
6a488035
TO
2016
2017 CRM_Core_DAO::executeQuery("DROP TRIGGER IF EXISTS $triggerName");
2018 CRM_Core_DAO::executeQuery(
2019 $triggerSQL,
2020 array(),
2021 TRUE,
2022 NULL,
2023 FALSE,
2024 FALSE
2025 );
2026 }
2027 }
2028 }
2029 }
2030
ffcef054
TO
2031 /**
2032 * Given a list of fields, create a list of references.
2033 *
6a0b768e
TO
2034 * @param string $className
2035 * BAO/DAO class name.
ffcef054
TO
2036 * @return array<CRM_Core_Reference_Interface>
2037 */
00be9182 2038 public static function createReferenceColumns($className) {
ffcef054
TO
2039 $result = array();
2040 $fields = $className::fields();
2041 foreach ($fields as $field) {
2042 if (isset($field['pseudoconstant'], $field['pseudoconstant']['optionGroupName'])) {
2043 $result[] = new CRM_Core_Reference_OptionValue(
2044 $className::getTableName(),
2045 $field['name'],
2046 'civicrm_option_value',
2047 CRM_Utils_Array::value('keyColumn', $field['pseudoconstant'], 'value'),
2048 $field['pseudoconstant']['optionGroupName']
2049 );
2050 }
2051 }
2052 return $result;
2053 }
2054
6a488035 2055 /**
71e5aa5c
ARW
2056 * Find all records which refer to this entity.
2057 *
a6c01b45 2058 * @return array
16b10e64 2059 * Array of objects referencing this
71e5aa5c 2060 */
00be9182 2061 public function findReferences() {
71e5aa5c
ARW
2062 $links = self::getReferencesToTable(static::getTableName());
2063
2064 $occurrences = array();
2065 foreach ($links as $refSpec) {
11626cf1 2066 /** @var $refSpec CRM_Core_Reference_Interface */
31bed28c 2067 $daoName = CRM_Core_DAO_AllCoreTables::getClassForTable($refSpec->getReferenceTable());
de49f39c 2068 $result = $refSpec->findReferences($this);
ffcef054
TO
2069 if ($result) {
2070 while ($result->fetch()) {
2071 $obj = new $daoName();
2072 $obj->id = $result->id;
2073 $occurrences[] = $obj;
2074 }
71e5aa5c
ARW
2075 }
2076 }
2077
2078 return $occurrences;
2079 }
2080
a0ee3941 2081 /**
a6c01b45
CW
2082 * @return array
2083 * each item has keys:
16b10e64
CW
2084 * - name: string
2085 * - type: string
2086 * - count: int
2087 * - table: string|null SQL table name
2088 * - key: string|null SQL column name
a0ee3941 2089 */
00be9182 2090 public function getReferenceCounts() {
1256c139
TO
2091 $links = self::getReferencesToTable(static::getTableName());
2092
2093 $counts = array();
2094 foreach ($links as $refSpec) {
2095 /** @var $refSpec CRM_Core_Reference_Interface */
2096 $count = $refSpec->getReferenceCount($this);
2097 if ($count['count'] != 0) {
2098 $counts[] = $count;
2099 }
2100 }
2101
91dee34b
TO
2102 foreach (CRM_Core_Component::getEnabledComponents() as $component) {
2103 /** @var $component CRM_Core_Component_Info */
2104 $counts = array_merge($counts, $component->getReferenceCounts($this));
2105 }
2106 CRM_Utils_Hook::referenceCounts($this, $counts);
2107
1256c139
TO
2108 return $counts;
2109 }
2110
71e5aa5c
ARW
2111 /**
2112 * List all tables which have hard foreign keys to this table.
6a488035 2113 *
71e5aa5c
ARW
2114 * For now, this returns a description of every entity_id/entity_table
2115 * reference.
2116 * TODO: filter dynamic entity references on the $tableName, based on
2117 * schema metadata in dynamicForeignKey which enumerates a restricted
2118 * set of possible entity_table's.
6a488035 2119 *
6a0b768e
TO
2120 * @param string $tableName
2121 * Table referred to.
6a488035 2122 *
a6c01b45
CW
2123 * @return array
2124 * structure of table and column, listing every table with a
16b10e64 2125 * foreign key reference to $tableName, and the column where the key appears.
6a488035 2126 */
00be9182 2127 public static function getReferencesToTable($tableName) {
71e5aa5c 2128 $refsFound = array();
31bed28c 2129 foreach (CRM_Core_DAO_AllCoreTables::getClasses() as $daoClassName) {
71e5aa5c
ARW
2130 $links = $daoClassName::getReferenceColumns();
2131 $daoTableName = $daoClassName::getTableName();
2132
2133 foreach ($links as $refSpec) {
11626cf1
TO
2134 /** @var $refSpec CRM_Core_Reference_Interface */
2135 if ($refSpec->matchesTargetTable($tableName)) {
71e5aa5c
ARW
2136 $refsFound[] = $refSpec;
2137 }
6a488035
TO
2138 }
2139 }
71e5aa5c 2140 return $refsFound;
6a488035 2141 }
032c9d10
TO
2142
2143 /**
2144 * Lookup the value of a MySQL global configuration variable.
2145 *
6a0b768e
TO
2146 * @param string $name
2147 * E.g. "thread_stack".
032c9d10
TO
2148 * @param mixed $default
2149 * @return mixed
2150 */
2151 public static function getGlobalSetting($name, $default = NULL) {
2152 // Alternatively, SELECT @@GLOBAL.thread_stack, but
2153 // that has been reported to fail under MySQL 5.0 for OS X
2154 $escapedName = self::escapeString($name);
2155 $dao = CRM_Core_DAO::executeQuery("SHOW VARIABLES LIKE '$escapedName'");
2156 if ($dao->fetch()) {
2157 return $dao->Value;
ab00f69d
DL
2158 }
2159 else {
032c9d10
TO
2160 return $default;
2161 }
2162 }
dc86f881
CW
2163
2164 /**
2165 * Get options for the called BAO object's field.
2166 * This function can be overridden by each BAO to add more logic related to context.
2158332a 2167 * The overriding function will generally call the lower-level CRM_Core_PseudoConstant::get
dc86f881 2168 *
2a3f958d 2169 * @param string $fieldName
6a0b768e 2170 * @param string $context
795492f3 2171 * @see CRM_Core_DAO::buildOptionsContext
6a0b768e 2172 * @param array $props
16b10e64 2173 * whatever is known about this bao object.
9a1b1948 2174 *
795492f3 2175 * @return array|bool
dc86f881
CW
2176 */
2177 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
2158332a 2178 // If a given bao does not override this function
dc86f881 2179 $baoName = get_called_class();
786ad6e1 2180 return CRM_Core_PseudoConstant::get($baoName, $fieldName, array(), $context);
dc86f881 2181 }
786ad6e1 2182
2a3f958d
CW
2183 /**
2184 * Populate option labels for this object's fields.
2185 *
2186 * @throws exception if called directly on the base class
2187 */
2188 public function getOptionLabels() {
2189 $fields = $this->fields();
2190 if ($fields === NULL) {
795492f3 2191 throw new Exception('Cannot call getOptionLabels on CRM_Core_DAO');
2a3f958d
CW
2192 }
2193 foreach ($fields as $field) {
2194 $name = CRM_Utils_Array::value('name', $field);
2195 if ($name && isset($this->$name)) {
a8c23526 2196 $label = CRM_Core_PseudoConstant::getLabel(get_class($this), $name, $this->$name);
2a3f958d
CW
2197 if ($label !== FALSE) {
2198 // Append 'label' onto the field name
2199 $labelName = $name . '_label';
2200 $this->$labelName = $label;
2201 }
2202 }
2203 }
2204 }
2205
786ad6e1
CW
2206 /**
2207 * Provides documentation and validation for the buildOptions $context param
2208 *
6a0b768e 2209 * @param string $context
77b97be7
EM
2210 *
2211 * @throws Exception
2212 * @return array
786ad6e1
CW
2213 */
2214 public static function buildOptionsContext($context = NULL) {
2215 $contexts = array(
a2407bc0
CW
2216 'get' => "get: all options are returned, even if they are disabled; labels are translated.",
2217 'create' => "create: options are filtered appropriately for the object being created/updated; labels are translated.",
2218 'search' => "search: searchable options are returned; labels are translated.",
2219 'validate' => "validate: all options are returned, even if they are disabled; machine names are used in place of labels.",
2220 'abbreviate' => "abbreviate: enabled options are returned; labels are replaced with abbreviations.",
2221 'match' => "match: enabled options are returned using machine names as keys; labels are translated.",
786ad6e1
CW
2222 );
2223 // Validation: enforce uniformity of this param
2224 if ($context !== NULL && !isset($contexts[$context])) {
395d8dc6 2225 throw new Exception("'$context' is not a valid context for buildOptions.");
786ad6e1
CW
2226 }
2227 return $contexts;
2228 }
2229
5fafc9b0 2230 /**
100fef9d 2231 * @param string $fieldName
5fafc9b0
CW
2232 * @return bool|array
2233 */
00be9182 2234 public function getFieldSpec($fieldName) {
5fafc9b0
CW
2235 $fields = $this->fields();
2236 $fieldKeys = $this->fieldKeys();
2237
2238 // Support "unique names" as well as sql names
2239 $fieldKey = $fieldName;
2240 if (empty($fields[$fieldKey])) {
2241 $fieldKey = CRM_Utils_Array::value($fieldName, $fieldKeys);
2242 }
2243 // If neither worked then this field doesn't exist. Return false.
2244 if (empty($fields[$fieldKey])) {
2245 return FALSE;
2246 }
2247 return $fields[$fieldKey];
2248 }
2249
faf8c53b 2250 /**
bb05da0c 2251 * Get SQL where clause for SQL filter syntax input parameters.
2252 *
faf8c53b 2253 * SQL version of api function to assign filters to the DAO based on the syntax
2254 * $field => array('IN' => array(4,6,9))
2255 * OR
2256 * $field => array('LIKE' => array('%me%))
2257 * etc
2258 *
6a0b768e
TO
2259 * @param string $fieldName
2260 * Name of fields.
5a4f6742
CW
2261 * @param array $filter
2262 * filter to be applied indexed by operator.
2263 * @param string $type
2264 * type of field (not actually used - nor in api @todo ).
2265 * @param string $alias
2266 * alternative field name ('as') @todo- not actually used.
6a0b768e
TO
2267 * @param bool $returnSanitisedArray
2268 * Return a sanitised array instead of a clause.
16b10e64 2269 * this is primarily so we can add filters @ the api level to the Query object based fields
9a1b1948
EM
2270 *
2271 * @throws Exception
c490a46a 2272 *
72b3a70c
CW
2273 * @return NULL|string|array
2274 * a string is returned if $returnSanitisedArray is not set, otherwise and Array or NULL
06f48f96 2275 * depending on whether it is supported as yet
9a1b1948 2276 */
3f2d6002 2277 public static function createSQLFilter($fieldName, $filter, $type, $alias = NULL, $returnSanitisedArray = FALSE) {
faf8c53b 2278 foreach ($filter as $operator => $criteria) {
6e23130a 2279 if (in_array($operator, self::acceptedSQLOperators(), TRUE)) {
faf8c53b 2280 switch ($operator) {
2281 // unary operators
faf8c53b 2282 case 'IS NULL':
2283 case 'IS NOT NULL':
c490a46a 2284 if (!$returnSanitisedArray) {
78c0bfc0 2285 return (sprintf('%s %s', $fieldName, $operator));
2286 }
c490a46a 2287 else {
a75c13cc 2288 return (sprintf('%s %s ', $fieldName, $operator));
06f48f96 2289 }
faf8c53b 2290 break;
2291
2292 // ternary operators
2293 case 'BETWEEN':
2294 case 'NOT BETWEEN':
2295 if (empty($criteria[0]) || empty($criteria[1])) {
395d8dc6 2296 throw new Exception("invalid criteria for $operator");
faf8c53b 2297 }
c490a46a 2298 if (!$returnSanitisedArray) {
78c0bfc0 2299 return (sprintf('%s ' . $operator . ' "%s" AND "%s"', $fieldName, CRM_Core_DAO::escapeString($criteria[0]), CRM_Core_DAO::escapeString($criteria[1])));
2300 }
c490a46a 2301 else {
06f48f96 2302 return NULL; // not yet implemented (tests required to implement)
2303 }
faf8c53b 2304 break;
2305
2306 // n-ary operators
2307 case 'IN':
2308 case 'NOT IN':
2309 if (empty($criteria)) {
395d8dc6 2310 throw new Exception("invalid criteria for $operator");
faf8c53b 2311 }
2312 $escapedCriteria = array_map(array(
2313 'CRM_Core_DAO',
795492f3 2314 'escapeString',
faf8c53b 2315 ), $criteria);
c490a46a 2316 if (!$returnSanitisedArray) {
78c0bfc0 2317 return (sprintf('%s %s ("%s")', $fieldName, $operator, implode('", "', $escapedCriteria)));
2318 }
2319 return $escapedCriteria;
faf8c53b 2320
2321 // binary operators
6a488035 2322
faf8c53b 2323 default:
c490a46a 2324 if (!$returnSanitisedArray) {
353ffa53 2325 return (sprintf('%s %s "%s"', $fieldName, $operator, CRM_Core_DAO::escapeString($criteria)));
78c0bfc0 2326 }
c490a46a 2327 else {
06f48f96 2328 return NULL; // not yet implemented (tests required to implement)
2329 }
faf8c53b 2330 }
2331 }
2332 }
2333 }
6842bb53 2334
e4176358
CW
2335 /**
2336 * @see http://issues.civicrm.org/jira/browse/CRM-9150
2337 * support for other syntaxes is discussed in ticket but being put off for now
2338 * @return array
2339 */
2340 public static function acceptedSQLOperators() {
353ffa53
TO
2341 return array(
2342 '=',
2343 '<=',
2344 '>=',
2345 '>',
2346 '<',
2347 'LIKE',
2348 "<>",
2349 "!=",
2350 "NOT LIKE",
2351 'IN',
2352 'NOT IN',
2353 'BETWEEN',
2354 'NOT BETWEEN',
2355 'IS NOT NULL',
795492f3 2356 'IS NULL',
353ffa53 2357 );
e4176358
CW
2358 }
2359
6842bb53
DL
2360 /**
2361 * SQL has a limit of 64 characters on various names:
2362 * table name, trigger name, column name ...
2363 *
2364 * For custom groups and fields we generated names from user entered input
2365 * which can be longer than this length, this function helps with creating
2366 * strings that meet various criteria.
2367 *
6a0b768e
TO
2368 * @param string $string
2369 * The string to be shortened.
2370 * @param int $length
2371 * The max length of the string.
9a1b1948
EM
2372 *
2373 * @param bool $makeRandom
2374 *
2375 * @return string
6842bb53
DL
2376 */
2377 public static function shortenSQLName($string, $length = 60, $makeRandom = FALSE) {
2378 // early return for strings that meet the requirements
2379 if (strlen($string) <= $length) {
2380 return $string;
2381 }
2382
2383 // easy return for calls that dont need a randomized uniq string
c490a46a 2384 if (!$makeRandom) {
6842bb53
DL
2385 return substr($string, 0, $length);
2386 }
2387
2388 // the string is longer than the length and we need a uniq string
b44e3f84 2389 // for the same tablename we need the same uniq string every time
6842bb53 2390 // hence we use md5 on the string, which is not random
a8dd306e
DL
2391 // we'll append 8 characters to the end of the tableName
2392 $md5string = substr(md5($string), 0, 8);
2393 return substr($string, 0, $length - 8) . "_{$md5string}";
6842bb53
DL
2394 }
2395
a0ee3941 2396 /**
c490a46a 2397 * @param array $params
a0ee3941 2398 */
353ffa53
TO
2399 public function setApiFilter(&$params) {
2400 }
6e1bb60c 2401
232624b1 2402}