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