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