3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
29 * Our base DAO class. All DAO classes should inherit from this class.
32 * @copyright CiviCRM LLC (c) 2004-2013
37 require_once 'PEAR.php';
38 require_once 'DB/DataObject.php';
40 require_once 'CRM/Core/I18n.php';
41 class CRM_Core_DAO
extends DB_DataObject
{
44 * a null object so we can pass it as reference if / when needed
46 static $_nullObject = NULL;
47 static $_nullArray = array();
49 static $_dbColumnValueCache = NULL;
50 CONST NOT_NULL
= 1, IS_NULL
= 2,
52 VALUE_SEPARATOR
= "\ 1",
53 BULK_INSERT_COUNT
= 200,
54 BULK_INSERT_HIGH_COUNT
= 200,
55 // special value for mail bulk inserts to avoid
56 // potential duplication, assuming a smaller number reduces number of queries
57 // by some factor, so some tradeoff. CRM-8678
58 BULK_MAIL_INSERT_COUNT
= 10,
59 QUERY_FORMAT_WILDCARD
= 1,
60 QUERY_FORMAT_NO_QUOTES
= 2;
63 * Define entities that shouldn't be created or deleted when creating/ deleting
64 * test objects - this prevents world regions, countries etc from being added / deleted
66 static $_testEntitiesToSkip = array();
68 * the factory class for this application
71 static $_factory = NULL;
79 function __construct() {
81 $this->__table
= $this->getTableName();
85 * empty definition for virtual function
87 static function getTableName() {
92 * initialize the DAO object
94 * @param string $dsn the database connection string
100 static function init($dsn) {
101 $options = &PEAR
::getStaticProperty('DB_DataObject', 'options');
102 $options['database'] = $dsn;
103 if (defined('CIVICRM_DAO_DEBUG')) {
104 self
::DebugLevel(CIVICRM_DAO_DEBUG
);
109 * reset the DAO object. DAO is kinda crappy in that there is an unwritten
110 * rule of one query per DAO. We attempt to get around this crappy restricrion
111 * by resetting some of DAO's internal fields. Use this with caution
119 foreach (array_keys($this->table()) as $field) {
120 unset($this->$field);
124 * reset the various DB_DAO structures manually
126 $this->_query
= array();
132 static function getLocaleTableName($tableName) {
135 $tables = CRM_Core_I18n_Schema
::schemaStructureTables();
136 if (in_array($tableName, $tables)) {
137 return $tableName . $dbLocale;
144 * Execute a query by the current DAO, localizing it along the way (if needed).
146 * @param string $query the SQL query for execution
147 * @param bool $i18nRewrite whether to rewrite the query
149 * @return object the current DAO object after the query execution
151 function query($query, $i18nRewrite = TRUE) {
152 // rewrite queries that should use $dbLocale-based views for multi-language installs
154 if ($i18nRewrite and $dbLocale) {
155 $query = CRM_Core_I18n_Schema
::rewriteQuery($query);
158 return parent
::query($query);
162 * Static function to set the factory instance for this class.
164 * @param object $factory the factory application object
170 static function setFactory(&$factory) {
171 self
::$_factory = &$factory;
175 * Factory method to instantiate a new object from a table name.
180 function factory($table = '') {
181 if (!isset(self
::$_factory)) {
182 return parent
::factory($table);
185 return self
::$_factory->create($table);
189 * Initialization for all DAO objects. Since we access DB_DO programatically
190 * we need to set the links manually.
195 function initialize() {
200 * Defines the default key as 'id'.
215 * Tells DB_DataObject which keys use autoincrement.
216 * 'id' is autoincrementing by default.
222 function sequenceKey() {
223 static $sequenceKeys;
224 if (!isset($sequenceKeys)) {
225 $sequenceKeys = array('id', TRUE);
227 return $sequenceKeys;
231 * returns list of FK relationships
236 * @return array of CRM_Core_EntityReference
238 static function getReferenceColumns() {
243 * returns all the column names of this table
249 static function &fields() {
255 $fields = &$this->fields();
259 foreach ($fields as $name => $value) {
260 $table[$value['name']] = $value['type'];
261 if (CRM_Utils_Array
::value('required', $value)) {
262 $table[$value['name']] +
= self
::DB_DAO_NOTNULL
;
271 if (!empty($this->id
)) {
279 CRM_Utils_Hook
::postSave($this);
284 function log($created = FALSE) {
287 if (!$this->getLog()) {
292 $session = CRM_Core_Session
::singleton();
293 $cid = $session->get('userID');
296 // return is we dont have handle to FK
301 $dao = new CRM_Core_DAO_Log();
302 $dao->entity_table
= $this->getTableName();
303 $dao->entity_id
= $this->id
;
304 $dao->modified_id
= $cid;
305 $dao->modified_date
= date("YmdHis");
310 * Given an associative array of name/value pairs, extract all the values
311 * that belong to this object and initialize the object with said values
313 * @param array $params (reference ) associative array of name/value pairs
315 * @return boolean did we copy all null values into the object
318 function copyValues(&$params) {
319 $fields = &$this->fields();
321 foreach ($fields as $name => $value) {
322 $dbName = $value['name'];
323 if (array_key_exists($dbName, $params)) {
324 $pValue = $params[$dbName];
327 elseif (array_key_exists($name, $params)) {
328 $pValue = $params[$name];
335 // if there is no value then make the variable NULL
337 if ($pValue === '') {
338 $this->$dbName = 'null';
341 $this->$dbName = $pValue;
350 * Store all the values from this object in an associative array
351 * this is a destructive store, calling function is responsible
352 * for keeping sanity of id's.
354 * @param object $object the object that we are extracting data from
355 * @param array $values (reference ) associative array of name/value pairs
361 static function storeValues(&$object, &$values) {
362 $fields = &$object->fields();
363 foreach ($fields as $name => $value) {
364 $dbName = $value['name'];
365 if (isset($object->$dbName) && $object->$dbName !== 'null') {
366 $values[$dbName] = $object->$dbName;
367 if ($name != $dbName) {
368 $values[$name] = $object->$dbName;
375 * create an attribute for this specific field. We only do this for strings and text
377 * @param array $field the field under task
379 * @return array|null the attributes for the object
383 static function makeAttribute($field) {
385 if (CRM_Utils_Array
::value('type', $field) == CRM_Utils_Type
::T_STRING
) {
386 $maxLength = CRM_Utils_Array
::value('maxlength', $field);
387 $size = CRM_Utils_Array
::value('size', $field);
388 if ($maxLength ||
$size) {
389 $attributes = array();
391 $attributes['maxlength'] = $maxLength;
394 $attributes['size'] = $size;
399 elseif (CRM_Utils_Array
::value('type', $field) == CRM_Utils_Type
::T_TEXT
) {
400 $rows = CRM_Utils_Array
::value('rows', $field);
404 $cols = CRM_Utils_Array
::value('cols', $field);
409 $attributes = array();
410 $attributes['rows'] = $rows;
411 $attributes['cols'] = $cols;
414 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
) {
415 $attributes['size'] = 6;
416 $attributes['maxlength'] = 14;
424 * Get the size and maxLength attributes for this text field
425 * (or for all text fields) in the DAO object.
427 * @param string $class name of DAO class
428 * @param string $fieldName field that i'm interested in or null if
429 * you want the attributes for all DAO text fields
431 * @return array assoc array of name => attribute pairs
435 static function getAttribute($class, $fieldName = NULL) {
436 $object = new $class( );
437 $fields = &$object->fields();
438 if ($fieldName != NULL) {
439 $field = CRM_Utils_Array
::value($fieldName, $fields);
440 return self
::makeAttribute($field);
443 $attributes = array();
444 foreach ($fields as $name => $field) {
445 $attribute = self
::makeAttribute($field);
447 $attributes[$name] = $attribute;
451 if (!empty($attributes)) {
458 static function transaction($type) {
459 CRM_Core_Error
::fatal('This function is obsolete, please use CRM_Core_Transaction');
463 * Check if there is a record with the same name in the db
465 * @param string $value the value of the field we are checking
466 * @param string $daoName the dao object name
467 * @param string $daoID the id of the object being updated. u can change your name
468 * as long as there is no conflict
469 * @param string $fieldName the name of the field in the DAO
471 * @return boolean true if object exists
475 static function objectExists($value, $daoName, $daoID, $fieldName = 'name') {
476 $object = new $daoName( );
477 $object->$fieldName = $value;
479 $config = CRM_Core_Config
::singleton();
481 if ($object->find(TRUE)) {
482 return ($daoID && $object->id
== $daoID) ?
TRUE : FALSE;
490 * Check if there is a given column in a specific table
492 * @param string $tableName
493 * @param string $columnName
494 * @param bool $i18nRewrite whether to rewrite the query on multilingual setups
496 * @return boolean true if exists, else false
499 static function checkFieldExists($tableName, $columnName, $i18nRewrite = TRUE) {
505 $params = array(1 => array($columnName, 'String'));
506 $dao = CRM_Core_DAO
::executeQuery($query, $params, TRUE, NULL, FALSE, $i18nRewrite);
507 $result = $dao->fetch() ?
TRUE : FALSE;
513 * Returns the storage engine used by given table-name(optional).
514 * Otherwise scans all the tables and return an array of all the
515 * distinct storage engines being used.
517 * @param string $tableName
522 static function getStorageValues($tableName = NULL, $maxTablesToCheck = 10, $fieldName = 'Engine') {
524 $query = "SHOW TABLE STATUS LIKE %1";
528 if (isset($tableName)) {
529 $params = array(1 => array($tableName, 'String'));
532 $params = array(1 => array('civicrm_%', 'String'));
535 $dao = CRM_Core_DAO
::executeQuery($query, $params);
538 while ($dao->fetch()) {
539 if (isset($values[$dao->$fieldName]) ||
540 // ignore import and other temp tables
541 strpos($dao->Name
, 'civicrm_import_job_') !== FALSE ||
542 strpos($dao->Name
, '_temp') !== FALSE
546 $values[$dao->$fieldName] = 1;
548 if ($maxTablesToCheck &&
549 $count >= $maxTablesToCheck
558 static function isDBMyISAM($maxTablesToCheck = 10) {
559 // show error if any of the tables, use 'MyISAM' storage engine.
560 $engines = self
::getStorageValues(NULL, $maxTablesToCheck);
561 if (array_key_exists('MyISAM', $engines)) {
568 * Checks if a constraint exists for a specified table.
570 * @param string $tableName
571 * @param string $constraint
573 * @return boolean true if constraint exists, false otherwise
576 static function checkConstraintExists($tableName, $constraint) {
577 static $show = array();
579 if (!array_key_exists($tableName, $show)) {
580 $query = "SHOW CREATE TABLE $tableName";
581 $dao = CRM_Core_DAO
::executeQuery($query);
583 if (!$dao->fetch()) {
584 CRM_Core_Error
::fatal();
588 $show[$tableName] = $dao->Create_Table
;
591 return preg_match("/\b$constraint\b/i", $show[$tableName]) ?
TRUE : FALSE;
595 * Checks if CONSTRAINT keyword exists for a specified table.
597 * @param string $tableName
599 * @return boolean true if CONSTRAINT keyword exists, false otherwise
601 function schemaRequiresRebuilding($tables = array("civicrm_contact")) {
603 foreach($tables as $tableName){
604 if (!array_key_exists($tableName, $show)) {
605 $query = "SHOW CREATE TABLE $tableName";
606 $dao = CRM_Core_DAO
::executeQuery($query);
608 if (!$dao->fetch()) {
609 CRM_Core_Error
::fatal();
613 $show[$tableName] = $dao->Create_Table
;
616 $result = preg_match("/\bCONSTRAINT\b\s/i", $show[$tableName]) ?
TRUE : FALSE;
628 * Checks if the FK constraint name is in the format 'FK_tableName_columnName'
629 * for a specified column of a table.
631 * @param string $tableName
632 * @param string $columnName
634 * @return boolean true if in format, false otherwise
637 static function checkFKConstraintInFormat($tableName, $columnName) {
638 static $show = array();
640 if (!array_key_exists($tableName, $show)) {
641 $query = "SHOW CREATE TABLE $tableName";
642 $dao = CRM_Core_DAO
::executeQuery($query);
644 if (!$dao->fetch()) {
645 CRM_Core_Error
::fatal();
649 $show[$tableName] = $dao->Create_Table
;
651 $constraint = "`FK_{$tableName}_{$columnName}`";
652 $pattern = "/\bCONSTRAINT\b\s+%s\s+\bFOREIGN\s+KEY\b\s/i";
653 return preg_match(sprintf($pattern, $constraint),$show[$tableName]) ?
TRUE : FALSE;
657 * Check whether a specific column in a specific table has always the same value
659 * @param string $tableName
660 * @param string $columnName
661 * @param string $columnValue
663 * @return boolean true if the value is always $columnValue, false otherwise
666 static function checkFieldHasAlwaysValue($tableName, $columnName, $columnValue) {
667 $query = "SELECT * FROM $tableName WHERE $columnName != '$columnValue'";
668 $dao = CRM_Core_DAO
::executeQuery($query);
669 $result = $dao->fetch() ?
FALSE : TRUE;
675 * Check whether a specific column in a specific table is always NULL
677 * @param string $tableName
678 * @param string $columnName
680 * @return boolean true if if the value is always NULL, false otherwise
683 static function checkFieldIsAlwaysNull($tableName, $columnName) {
684 $query = "SELECT * FROM $tableName WHERE $columnName IS NOT NULL";
685 $dao = CRM_Core_DAO
::executeQuery($query);
686 $result = $dao->fetch() ?
FALSE : TRUE;
692 * Check if there is a given table in the database
694 * @param string $tableName
696 * @return boolean true if exists, else false
699 static function checkTableExists($tableName) {
704 $params = array(1 => array($tableName, 'String'));
706 $dao = CRM_Core_DAO
::executeQuery($query, $params);
707 $result = $dao->fetch() ?
TRUE : FALSE;
712 function checkVersion($version) {
717 $dbVersion = CRM_Core_DAO
::singleValueQuery($query);
718 return trim($version) == trim($dbVersion) ?
TRUE : FALSE;
722 * Given a DAO name, a column name and a column value, find the record and GET the value of another column in that record
724 * @param string $daoName Name of the DAO (Example: CRM_Contact_DAO_Contact to retrieve value from a contact)
725 * @param int $searchValue Value of the column you want to search by
726 * @param string $returnColumn Name of the column you want to GET the value of
727 * @param string $searchColumn Name of the column you want to search by
728 * @param boolean $force Skip use of the cache
730 * @return string|null Value of $returnColumn in the retrieved record
734 static function getFieldValue($daoName, $searchValue, $returnColumn = 'name', $searchColumn = 'id', $force = FALSE) {
736 empty($searchValue) ||
737 trim(strtolower($searchValue)) == 'null'
739 // adding this year since developers forget to check for an id
740 // or for the 'null' (which is a bad DAO kludge)
741 // and hence we get the first value in the db
742 CRM_Core_Error
::fatal();
745 $cacheKey = "{$daoName}:{$searchValue}:{$returnColumn}:{$searchColumn}";
746 if (self
::$_dbColumnValueCache === NULL) {
747 self
::$_dbColumnValueCache = array();
750 if (!array_key_exists($cacheKey, self
::$_dbColumnValueCache) ||
$force) {
751 $object = new $daoName( );
752 $object->$searchColumn = $searchValue;
753 $object->selectAdd();
754 $object->selectAdd($returnColumn);
757 if ($object->find(TRUE)) {
758 $result = $object->$returnColumn;
762 self
::$_dbColumnValueCache[$cacheKey] = $result;
764 return self
::$_dbColumnValueCache[$cacheKey];
768 * Given a DAO name, a column name and a column value, find the record and SET the value of another column in that record
770 * @param string $daoName Name of the DAO (Example: CRM_Contact_DAO_Contact to retrieve value from a contact)
771 * @param int $searchValue Value of the column you want to search by
772 * @param string $setColumn Name of the column you want to SET the value of
773 * @param string $setValue SET the setColumn to this value
774 * @param string $searchColumn Name of the column you want to search by
776 * @return boolean true if we found and updated the object, else false
780 static function setFieldValue($daoName, $searchValue, $setColumn, $setValue, $searchColumn = 'id') {
781 $object = new $daoName( );
782 $object->selectAdd();
783 $object->selectAdd("$searchColumn, $setColumn");
784 $object->$searchColumn = $searchValue;
786 if ($object->find(TRUE)) {
787 $object->$setColumn = $setValue;
788 if ($object->save()) {
799 * @param array|object $sort either array or CRM_Utils_Sort
800 * @param string $default - default sort value
802 * @return string - sortString
806 static function getSortString($sort, $default = NULL) {
807 // check if sort is of type CRM_Utils_Sort
808 if (is_a($sort, 'CRM_Utils_Sort')) {
809 return $sort->orderBy();
812 // is it an array specified as $field => $sortDirection ?
814 foreach ($sort as $k => $v) {
815 $sortString .= "$k $v,";
817 return rtrim($sortString, ',');
823 * Takes a bunch of params that are needed to match certain criteria and
824 * retrieves the relevant objects. Typically the valid params are only
825 * contact_id. We'll tweak this function to be more full featured over a period
826 * of time. This is the inverse function of create. It also stores all the retrieved
827 * values in the default array
829 * @param string $daoName name of the dao object
830 * @param array $params (reference ) an assoc array of name/value pairs
831 * @param array $defaults (reference ) an assoc array to hold the flattened values
832 * @param array $returnProperities an assoc array of fields that need to be returned, eg array( 'first_name', 'last_name')
834 * @return object an object of type referenced by daoName
838 static function commonRetrieve($daoName, &$params, &$defaults, $returnProperities = NULL) {
839 $object = new $daoName( );
840 $object->copyValues($params);
842 // return only specific fields if returnproperties are sent
843 if (!empty($returnProperities)) {
844 $object->selectAdd();
845 $object->selectAdd(implode(',', $returnProperities));
848 if ($object->find(TRUE)) {
849 self
::storeValues($object, $defaults);
856 * Delete the object records that are associated with this contact
858 * @param string $daoName name of the dao object
859 * @param int $contactId id of the contact to delete
865 static function deleteEntityContact($daoName, $contactId) {
866 $object = new $daoName( );
868 $object->entity_table
= 'civicrm_contact';
869 $object->entity_id
= $contactId;
876 * @param string $query query to be executed
878 * @return Object CRM_Core_DAO object that holds the results of the query
882 static function &executeQuery(
889 $trapException = FALSE
891 $queryStr = self
::composeQuery($query, $params, $abort);
892 //CRM_Core_Error::debug( 'q', $queryStr );
895 $dao = new CRM_Core_DAO();
898 $dao = new $daoName( );
901 if ($trapException) {
902 CRM_Core_Error
::ignoreException();
905 $result = $dao->query($queryStr, $i18nRewrite);
907 if ($trapException) {
908 CRM_Core_Error
::setCallback();
911 if (is_a($result, 'DB_Error')) {
916 preg_match('/^(insert|update|delete|create|drop|replace)/i', $queryStr)
918 // we typically do this for insert/update/delete stataments OR if explicitly asked to
926 * execute a query and get the single result
928 * @param string $query query to be executed
930 * @return string the result of the query
934 static function &singleValueQuery($query,
939 $queryStr = self
::composeQuery($query, $params, $abort);
944 $_dao = new CRM_Core_DAO();
947 $_dao->query($queryStr, $i18nRewrite);
949 $result = $_dao->getDatabaseResult();
952 $row = $result->fetchRow();
961 static function composeQuery($query, &$params, $abort = TRUE) {
963 foreach ($params as $key => $item) {
964 if (is_numeric($key)) {
965 if (CRM_Utils_Type
::validate($item[0], $item[1]) !== NULL) {
966 $item[0] = self
::escapeString($item[0]);
967 if ($item[1] == 'String' ||
968 $item[1] == 'Memo' ||
971 // Support class constants stipulating wildcard characters and/or
972 // non-quoting of strings. Also support legacy code which may be
973 // passing in TRUE or 1 for $item[2], which used to indicate the
974 // use of wildcard characters.
975 if (!empty($item[2])) {
976 if ($item[2] & CRM_Core_DAO
::QUERY_FORMAT_WILDCARD ||
$item[2] === TRUE) {
977 $item[0] = "'%{$item[0]}%'";
979 elseif (!($item[2] & CRM_Core_DAO
::QUERY_FORMAT_NO_QUOTES
)) {
980 $item[0] = "'{$item[0]}'";
984 $item[0] = "'{$item[0]}'";
988 if (($item[1] == 'Date' ||
$item[1] == 'Timestamp') &&
989 strlen($item[0]) == 0
994 $tr['%' . $key] = $item[0];
997 CRM_Core_Error
::fatal("{$item[0]} is not of type {$item[1]}");
1002 return strtr($query, $tr);
1005 static function freeResult($ids = NULL) {
1006 global $_DB_DATAOBJECT;
1010 foreach ( array_keys( $_DB_DATAOBJECT['RESULTS'] ) as $id ) {
1011 $q[] = $_DB_DATAOBJECT['RESULTS'][$id]->query;
1013 CRM_Core_Error::debug( 'k', $q );
1018 if (!$_DB_DATAOBJECT ||
1019 !isset($_DB_DATAOBJECT['RESULTS'])
1023 $ids = array_keys($_DB_DATAOBJECT['RESULTS']);
1026 foreach ($ids as $id) {
1027 if (isset($_DB_DATAOBJECT['RESULTS'][$id])) {
1028 if (is_resource($_DB_DATAOBJECT['RESULTS'][$id]->result
)) {
1029 mysql_free_result($_DB_DATAOBJECT['RESULTS'][$id]->result
);
1031 unset($_DB_DATAOBJECT['RESULTS'][$id]);
1034 if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$id])) {
1035 unset($_DB_DATAOBJECT['RESULTFIELDS'][$id]);
1041 * This function is to make a shallow copy of an object
1042 * and all the fields in the object
1044 * @param string $daoName name of the dao
1045 * @param array $criteria array of all the fields & values
1046 * on which basis to copy
1047 * @param array $newData array of all the fields & values
1048 * to be copied besides the other fields
1049 * @param string $fieldsFix array of fields that you want to prefix/suffix/replace
1050 * @param string $blockCopyOfDependencies fields that you want to block from
1054 * @return (reference ) the newly created copy of the object
1057 static function ©Generic($daoName, $criteria, $newData = NULL, $fieldsFix = NULL, $blockCopyOfDependencies = NULL) {
1058 $object = new $daoName( );
1060 $object->id
= $criteria['id'];
1063 foreach ($criteria as $key => $value) {
1064 $object->$key = $value;
1069 while ($object->fetch()) {
1071 // all the objects except with $blockCopyOfDependencies set
1072 // be copied - addresses #CRM-1962
1074 if ($blockCopyOfDependencies && $object->$blockCopyOfDependencies) {
1078 $newObject = new $daoName( );
1080 $fields = &$object->fields();
1081 if (!is_array($fieldsFix)) {
1082 $fieldsToPrefix = array();
1083 $fieldsToSuffix = array();
1084 $fieldsToReplace = array();
1086 if (CRM_Utils_Array
::value('prefix', $fieldsFix)) {
1087 $fieldsToPrefix = $fieldsFix['prefix'];
1089 if (CRM_Utils_Array
::value('suffix', $fieldsFix)) {
1090 $fieldsToSuffix = $fieldsFix['suffix'];
1092 if (CRM_Utils_Array
::value('replace', $fieldsFix)) {
1093 $fieldsToReplace = $fieldsFix['replace'];
1096 foreach ($fields as $name => $value) {
1097 if ($name == 'id' ||
$value['name'] == 'id') {
1098 // copy everything but the id!
1102 $dbName = $value['name'];
1103 $newObject->$dbName = $object->$dbName;
1104 if (isset($fieldsToPrefix[$dbName])) {
1105 $newObject->$dbName = $fieldsToPrefix[$dbName] . $newObject->$dbName;
1107 if (isset($fieldsToSuffix[$dbName])) {
1108 $newObject->$dbName .= $fieldsToSuffix[$dbName];
1110 if (isset($fieldsToReplace[$dbName])) {
1111 $newObject->$dbName = $fieldsToReplace[$dbName];
1114 if (substr($name, -5) == '_date' ||
1115 substr($name, -10) == '_date_time'
1117 $newObject->$dbName = CRM_Utils_Date
::isoToMysql($newObject->$dbName);
1121 foreach ($newData as $k => $v) {
1122 $newObject->$k = $v;
1132 * Given the component id, compute the contact id
1133 * since its used for things like send email
1135 public static function &getContactIDsFromComponent(&$componentIDs, $tableName) {
1136 $contactIDs = array();
1138 if (empty($componentIDs)) {
1142 $IDs = implode(',', $componentIDs);
1146 WHERE id IN ( $IDs )
1149 $dao = CRM_Core_DAO
::executeQuery($query);
1150 while ($dao->fetch()) {
1151 $contactIDs[] = $dao->contact_id
;
1157 * Takes a bunch of params that are needed to match certain criteria and
1158 * retrieves the relevant objects. Typically the valid params are only
1159 * contact_id. We'll tweak this function to be more full featured over a period
1160 * of time. This is the inverse function of create. It also stores all the retrieved
1161 * values in the default array
1163 * @param string $daoName name of the dao object
1164 * @param array $params (reference ) an assoc array of name/value pairs
1165 * @param array $defaults (reference ) an assoc array to hold the flattened values
1166 * @param array $returnProperities an assoc array of fields that need to be returned, eg array( 'first_name', 'last_name')
1168 * @return object an object of type referenced by daoName
1172 static function commonRetrieveAll($daoName, $fieldIdName = 'id', $fieldId, &$details, $returnProperities = NULL) {
1173 require_once (str_replace('_', DIRECTORY_SEPARATOR
, $daoName) . ".php");
1174 $object = new $daoName( );
1175 $object->$fieldIdName = $fieldId;
1177 // return only specific fields if returnproperties are sent
1178 if (!empty($returnProperities)) {
1179 $object->selectAdd();
1180 $object->selectAdd('id');
1181 $object->selectAdd(implode(',', $returnProperities));
1185 while ($object->fetch()) {
1186 $defaults = array();
1187 self
::storeValues($object, $defaults);
1188 $details[$object->id
] = $defaults;
1194 static function dropAllTables() {
1196 // first drop all the custom tables we've created
1197 CRM_Core_BAO_CustomGroup
::dropAllTables();
1199 // drop all multilingual views
1200 CRM_Core_I18n_Schema
::dropAllViews();
1202 CRM_Utils_File
::sourceSQLFile(CIVICRM_DSN
,
1203 dirname(__FILE__
) . DIRECTORY_SEPARATOR
.
1204 '..' . DIRECTORY_SEPARATOR
.
1205 '..' . DIRECTORY_SEPARATOR
.
1206 'sql' . DIRECTORY_SEPARATOR
.
1207 'civicrm_drop.mysql'
1211 static function escapeString($string) {
1212 static $_dao = NULL;
1215 $_dao = new CRM_Core_DAO();
1218 return $_dao->escape($string);
1222 * Escape a list of strings for use with "WHERE X IN (...)" queries.
1224 * @param $strings array
1225 * @param $default string the value to use if $strings has no elements
1226 * @return string eg "abc","def","ghi"
1228 static function escapeStrings($strings, $default = NULL) {
1229 static $_dao = NULL;
1231 $_dao = new CRM_Core_DAO();
1234 if (empty($strings)) {
1238 $escapes = array_map(array($_dao, 'escape'), $strings);
1239 return '"' . implode('","', $escapes) . '"';
1242 static function escapeWildCardString($string) {
1244 // ensure we escape the single characters % and _ which are mysql wild
1245 // card characters and could come in via sortByCharacter
1246 // note that mysql does not escape these characters
1247 if ($string && in_array($string,
1248 array('%', '_', '%%', '_%')
1250 return '\\' . $string;
1253 return self
::escapeString($string);
1256 //Creates a test object, including any required objects it needs via recursion
1257 //createOnly: only create in database, do not store or return the objects (useful for perf testing)
1258 //ONLY USE FOR TESTING
1259 static function createTestObject(
1265 static $counter = 0;
1266 CRM_Core_DAO
::$_testEntitiesToSkip = array(
1267 'CRM_Core_DAO_Worldregion',
1268 'CRM_Core_DAO_StateProvince',
1269 'CRM_Core_DAO_Country',
1270 'CRM_Core_DAO_Domain',
1273 for ($i = 0; $i < $numObjects; ++
$i) {
1276 $object = new $daoName();
1278 $fields = &$object->fields();
1279 foreach ($fields as $name => $value) {
1280 $dbName = $value['name'];
1281 if($dbName == 'contact_sub_type' && empty($params['contact_sub_type'])){
1282 //coming up with a rule to set this is too complex let's not set it
1285 $FKClassName = CRM_Utils_Array
::value('FKClassName', $value);
1286 $required = CRM_Utils_Array
::value('required', $value);
1287 if (CRM_Utils_Array
::value($dbName, $params) !== NULL && !is_array($params[$dbName])) {
1288 $object->$dbName = $params[$dbName];
1291 elseif ($dbName != 'id') {
1292 if ($FKClassName != NULL) {
1293 //skip the FK if it is not required
1294 // if it's contact id we should create even if not required
1295 // we'll have a go @ fetching first though
1296 if (!$required && $dbName != 'contact_id') {
1297 $fkDAO = new $FKClassName;
1298 if($fkDAO->find(TRUE)){
1299 $object->$dbName = $fkDAO->id
;
1304 if(in_array($FKClassName, CRM_Core_DAO
::$_testEntitiesToSkip)){
1305 $depObject = new $FKClassName();
1306 $depObject->find(TRUE);
1307 } elseif ($daoName == 'CRM_Member_DAO_MembershipType' && $name == 'member_of_contact_id') {
1308 // FIXME: the fields() metadata is not specific enough
1309 $depObject = CRM_Core_DAO
::createTestObject($FKClassName, array('contact_type' => 'Organization'));
1311 //if it is required we need to generate the dependency object first
1312 $depObject = CRM_Core_DAO
::createTestObject($FKClassName, CRM_Utils_Array
::value($dbName, $params, 1));
1314 $object->$dbName = $depObject->id
;
1319 // Pick an option value if needed
1320 if ($value['type'] !== CRM_Utils_Type
::T_BOOLEAN
) {
1321 $options = $daoName::buildOptions($dbName, 'create');
1323 $object->$dbName = key($options);
1328 switch ($value['type']) {
1329 case CRM_Utils_Type
::T_INT
:
1330 case CRM_Utils_Type
::T_FLOAT
:
1331 case CRM_Utils_Type
::T_MONEY
:
1332 $object->$dbName = $counter;
1335 case CRM_Utils_Type
::T_BOOLEAN
:
1336 if (isset($value['default'])) {
1337 $object->$dbName = $value['default'];
1339 elseif ($value['name'] == 'is_deleted' ||
$value['name'] == 'is_test') {
1340 $object->$dbName = 0;
1343 $object->$dbName = 1;
1347 case CRM_Utils_Type
::T_DATE
:
1348 case CRM_Utils_Type
::T_TIMESTAMP
:
1349 $object->$dbName = '19700101';
1352 case CRM_Utils_Type
::T_TIME
:
1353 CRM_Core_Error
::fatal('T_TIME shouldnt be used.');
1354 //$object->$dbName='000000';
1356 case CRM_Utils_Type
::T_CCNUM
:
1357 $object->$dbName = '4111 1111 1111 1111';
1360 case CRM_Utils_Type
::T_URL
:
1361 $object->$dbName = 'http://www.civicrm.org';
1364 case CRM_Utils_Type
::T_STRING
:
1365 case CRM_Utils_Type
::T_BLOB
:
1366 case CRM_Utils_Type
::T_MEDIUMBLOB
:
1367 case CRM_Utils_Type
::T_TEXT
:
1368 case CRM_Utils_Type
::T_LONGTEXT
:
1369 case CRM_Utils_Type
::T_EMAIL
:
1371 if (isset($value['enumValues'])) {
1372 if (isset($value['default'])) {
1373 $object->$dbName = $value['default'];
1376 if (is_array($value['enumValues'])) {
1377 $object->$dbName = $value['enumValues'][0];
1380 $defaultValues = explode(',', $value['enumValues']);
1381 $object->$dbName = $defaultValues[0];
1386 $object->$dbName = $dbName . '_' . $counter;
1387 $maxlength = CRM_Utils_Array
::value('maxlength', $value);
1388 if ($maxlength > 0 && strlen($object->$dbName) > $maxlength) {
1389 $object->$dbName = substr($object->$dbName, 0, $value['maxlength']);
1399 $objects[$i] = $object;
1402 else unset($object);
1410 elseif ($numObjects == 1) { return $objects[0];}
1411 else return $objects;
1414 //deletes the this object plus any dependent objects that are associated with it
1415 //ONLY USE FOR TESTING
1417 static function deleteTestObjects($daoName, $params = array(
1420 $object = new $daoName ( );
1421 $object->id
= CRM_Utils_Array
::value('id', $params);
1423 $deletions = array(); // array(array(0 => $daoName, 1 => $daoParams))
1424 if ($object->find(TRUE)) {
1426 $fields = &$object->fields();
1427 foreach ($fields as $name => $value) {
1429 $dbName = $value['name'];
1431 $FKClassName = CRM_Utils_Array
::value('FKClassName', $value);
1432 $required = CRM_Utils_Array
::value('required', $value);
1433 if ($FKClassName != NULL
1435 && !in_array($FKClassName, CRM_Core_DAO
::$_testEntitiesToSkip)
1436 && ($required ||
$dbName == 'contact_id')) {
1437 $deletions[] = array($FKClassName, array('id' => $object->$dbName)); // x
1444 foreach ($deletions as $deletion) {
1445 CRM_Core_DAO
::deleteTestObjects($deletion[0], $deletion[1]);
1449 static function createTempTableName($prefix = 'civicrm', $addRandomString = TRUE, $string = NULL) {
1450 $tableName = $prefix . "_temp";
1452 if ($addRandomString) {
1454 $tableName .= "_" . $string;
1457 $tableName .= "_" . md5(uniqid('', TRUE));
1463 static function checkTriggerViewPermission($view = TRUE, $trigger = TRUE) {
1464 // test for create view and trigger permissions and if allowed, add the option to go multilingual
1466 // I'm not sure why we use the getStaticProperty for an error, rather than checking for DB_Error
1467 CRM_Core_Error
::ignoreException();
1468 $dao = new CRM_Core_DAO();
1470 $dao->query('CREATE OR REPLACE VIEW civicrm_domain_view AS SELECT * FROM civicrm_domain');
1471 if (PEAR
::getStaticProperty('DB_DataObject', 'lastError')) {
1472 CRM_Core_Error
::setCallback();
1478 $result = $dao->query('CREATE TRIGGER civicrm_domain_trigger BEFORE INSERT ON civicrm_domain FOR EACH ROW BEGIN END');
1479 if (PEAR
::getStaticProperty('DB_DataObject', 'lastError') ||
is_a($result, 'DB_Error')) {
1480 CRM_Core_Error
::setCallback();
1482 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1487 $dao->query('DROP TRIGGER IF EXISTS civicrm_domain_trigger');
1488 if (PEAR
::getStaticProperty('DB_DataObject', 'lastError')) {
1489 CRM_Core_Error
::setCallback();
1491 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1498 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1499 if (PEAR
::getStaticProperty('DB_DataObject', 'lastError')) {
1500 CRM_Core_Error
::setCallback();
1504 CRM_Core_Error
::setCallback();
1509 static function debugPrint($message = NULL, $printDAO = TRUE) {
1510 CRM_Utils_System
::xMemory("{$message}: ");
1513 global $_DB_DATAOBJECT;
1515 foreach (array_keys($_DB_DATAOBJECT['RESULTS']) as $id) {
1516 $q[] = $_DB_DATAOBJECT['RESULTS'][$id]->query
;
1518 CRM_Core_Error
::debug('_DB_DATAOBJECT', $q);
1523 * Build a list of triggers via hook and add them to (err, reconcile them
1524 * with) the database.
1526 * @param $tableName string the specific table requiring a rebuild; or NULL to rebuild all tables
1529 static function triggerRebuild($tableName = NULL) {
1532 $logging = new CRM_Logging_Schema
;
1533 $logging->triggerInfo($info, $tableName);
1535 CRM_Core_I18n_Schema
::triggerInfo($info, $tableName);
1536 CRM_Contact_BAO_Contact
::triggerInfo($info, $tableName);
1538 CRM_Utils_Hook
::triggerInfo($info, $tableName);
1540 // drop all existing triggers on all tables
1541 $logging->dropTriggers($tableName);
1543 // now create the set of new triggers
1544 self
::createTriggers($info);
1548 * Wrapper function to drop triggers
1550 * @param $tableName string the specific table requiring a rebuild; or NULL to rebuild all tables
1552 static function dropTriggers($tableName = NULL) {
1555 $logging = new CRM_Logging_Schema
;
1556 $logging->triggerInfo($info, $tableName);
1558 // drop all existing triggers on all tables
1559 $logging->dropTriggers($tableName);
1563 * @param $info array per hook_civicrm_triggerInfo
1564 * @param $onlyTableName string the specific table requiring a rebuild; or NULL to rebuild all tables
1566 static function createTriggers(&$info, $onlyTableName = NULL) {
1567 // Validate info array, should probably raise errors?
1568 if (is_array($info) == FALSE) {
1572 $triggers = array();
1574 // now enumerate the tables and the events and collect the same set in a different format
1575 foreach ($info as $value) {
1577 // clean the incoming data, skip malformed entries
1578 // TODO: malformed entries should raise errors or get logged.
1579 if (isset($value['table']) == FALSE ||
1580 isset($value['event']) == FALSE ||
1581 isset($value['when']) == FALSE ||
1582 isset($value['sql']) == FALSE
1587 if (is_string($value['table']) == TRUE) {
1588 $tables = array($value['table']);
1591 $tables = $value['table'];
1594 if (is_string($value['event']) == TRUE) {
1595 $events = array(strtolower($value['event']));
1598 $events = array_map('strtolower', $value['event']);
1601 $whenName = strtolower($value['when']);
1603 foreach ($tables as $tableName) {
1604 if (!isset($triggers[$tableName])) {
1605 $triggers[$tableName] = array();
1608 foreach ($events as $eventName) {
1609 $template_params = array('{tableName}', '{eventName}');
1610 $template_values = array($tableName, $eventName);
1612 $sql = str_replace($template_params,
1616 $variables = str_replace($template_params,
1618 CRM_Utils_Array
::value('variables', $value)
1621 if (!isset($triggers[$tableName][$eventName])) {
1622 $triggers[$tableName][$eventName] = array();
1625 if (!isset($triggers[$tableName][$eventName][$whenName])) {
1626 // We're leaving out cursors, conditions, and handlers for now
1627 // they are kind of dangerous in this context anyway
1628 // better off putting them in stored procedures
1629 $triggers[$tableName][$eventName][$whenName] = array(
1630 'variables' => array(),
1636 $triggers[$tableName][$eventName][$whenName]['variables'][] = $variables;
1639 $triggers[$tableName][$eventName][$whenName]['sql'][] = $sql;
1644 // now spit out the sql
1645 foreach ($triggers as $tableName => $tables) {
1646 if ($onlyTableName != NULL && $onlyTableName != $tableName) {
1649 foreach ($tables as $eventName => $events) {
1650 foreach ($events as $whenName => $parts) {
1651 $varString = implode("\n", $parts['variables']);
1652 $sqlString = implode("\n", $parts['sql']);
1653 $triggerName = "{$tableName}_{$whenName}_{$eventName}";
1654 $triggerSQL = "CREATE TRIGGER $triggerName $whenName $eventName ON $tableName FOR EACH ROW BEGIN $varString $sqlString END";
1656 CRM_Core_DAO
::executeQuery("DROP TRIGGER IF EXISTS $triggerName");
1657 CRM_Core_DAO
::executeQuery(
1671 * Find all records which refer to this entity.
1673 * @return array of objects referencing this
1675 function findReferences() {
1676 $links = self
::getReferencesToTable(static::getTableName());
1678 $occurrences = array();
1679 foreach ($links as $refSpec) {
1680 $refColumn = $refSpec->getReferenceKey();
1681 $targetColumn = $refSpec->getTargetKey();
1682 $params = array(1 => array($this->$targetColumn, 'String'));
1685 FROM {$refSpec->getReferenceTable()}
1686 WHERE {$refColumn} = %1
1688 if ($refSpec->isGeneric()) {
1689 $params[2] = array(static::getTableName(), 'String');
1691 AND {$refSpec->getTypeColumn()} = %2
1694 $daoName = CRM_Core_DAO_AllCoreTables
::getClassForTable($refSpec->getReferenceTable());
1695 $result = self
::executeQuery($sql, $params, TRUE, $daoName);
1696 while ($result->fetch()) {
1697 $obj = new $daoName();
1698 $obj->id
= $result->id
;
1699 $occurrences[] = $obj;
1703 return $occurrences;
1707 * List all tables which have hard foreign keys to this table.
1709 * For now, this returns a description of every entity_id/entity_table
1711 * TODO: filter dynamic entity references on the $tableName, based on
1712 * schema metadata in dynamicForeignKey which enumerates a restricted
1713 * set of possible entity_table's.
1715 * @param string $tableName table referred to
1717 * @return array structure of table and column, listing every table with a
1718 * foreign key reference to $tableName, and the column where the key appears.
1720 static function getReferencesToTable($tableName) {
1721 $refsFound = array();
1722 foreach (CRM_Core_DAO_AllCoreTables
::getClasses() as $daoClassName) {
1723 $links = $daoClassName::getReferenceColumns();
1724 $daoTableName = $daoClassName::getTableName();
1726 foreach ($links as $refSpec) {
1727 if ($refSpec->getTargetTable() === $tableName
1728 or $refSpec->isGeneric()
1730 $refsFound[] = $refSpec;
1738 * Lookup the value of a MySQL global configuration variable.
1740 * @param string $name e.g. "thread_stack"
1741 * @param mixed $default
1744 public static function getGlobalSetting($name, $default = NULL) {
1745 // Alternatively, SELECT @@GLOBAL.thread_stack, but
1746 // that has been reported to fail under MySQL 5.0 for OS X
1747 $escapedName = self
::escapeString($name);
1748 $dao = CRM_Core_DAO
::executeQuery("SHOW VARIABLES LIKE '$escapedName'");
1749 if ($dao->fetch()) {
1758 * Get options for the called BAO object's field.
1759 * This function can be overridden by each BAO to add more logic related to context.
1760 * The overriding function will generally call the lower-level CRM_Core_PseudoConstant::get
1762 * @param string $fieldName
1763 * @param string $context: @see CRM_Core_DAO::buildOptionsContext
1764 * @param array $props: whatever is known about this bao object
1766 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
1767 // If a given bao does not override this function
1768 $baoName = get_called_class();
1769 return CRM_Core_PseudoConstant
::get($baoName, $fieldName, array(), $context);
1773 * Populate option labels for this object's fields.
1775 * @throws exception if called directly on the base class
1777 public function getOptionLabels() {
1778 $fields = $this->fields();
1779 if ($fields === NULL) {
1780 throw new exception ('Cannot call getOptionLabels on CRM_Core_DAO');
1782 foreach ($fields as $field) {
1783 $name = CRM_Utils_Array
::value('name', $field);
1784 if ($name && isset($this->$name)) {
1785 $label = CRM_Core_PseudoConstant
::getLabel(get_class($this), $name, $this->$name);
1786 if ($label !== FALSE) {
1787 // Append 'label' onto the field name
1788 $labelName = $name . '_label';
1789 $this->$labelName = $label;
1796 * Provides documentation and validation for the buildOptions $context param
1798 * @param String $context
1800 public static function buildOptionsContext($context = NULL) {
1802 'get' => "All options are returned, even if they are disabled. Labels are translated.",
1803 'create' => "Options are filtered appropriately for the object being created/updated. Labels are translated.",
1804 'search' => "Searchable options are returned. Labels are translated.",
1805 'validate' => "All options are returned, even if they are disabled. Machine names are used in place of labels.",
1807 // Validation: enforce uniformity of this param
1808 if ($context !== NULL && !isset($contexts[$context])) {
1809 throw new exception("'$context' is not a valid context for buildOptions.");
1815 * SQL version of api function to assign filters to the DAO based on the syntax
1816 * $field => array('IN' => array(4,6,9))
1818 * $field => array('LIKE' => array('%me%))
1821 * @param $fieldname string name of fields
1822 * @param $filter array filter to be applied indexed by operator
1823 * @param $type String type of field (not actually used - nor in api @todo )
1824 * @param $alias String alternative field name ('as') @todo- not actually used
1826 public function createSQLFilter($fieldName, $filter, $type, $alias = NULL) {
1827 // http://issues.civicrm.org/jira/browse/CRM-9150 - stick with 'simple' operators for now
1828 // support for other syntaxes is discussed in ticket but being put off for now
1829 $acceptedSQLOperators = array('=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=", "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN');
1830 foreach ($filter as $operator => $criteria) {
1831 if (in_array($operator, $acceptedSQLOperators)) {
1832 switch ($operator) {
1836 return (sprintf('%s %s', $fieldName, $operator));
1839 // ternary operators
1842 if (empty($criteria[0]) ||
empty($criteria[1])) {
1843 throw new exception("invalid criteria for $operator");
1845 return (sprintf('%s ' . $operator . ' "%s" AND "%s"', $fieldName, CRM_Core_DAO
::escapeString($criteria[0]), CRM_Core_DAO
::escapeString($criteria[1])));
1851 if (empty($criteria)) {
1852 throw new exception("invalid criteria for $operator");
1854 $escapedCriteria = array_map(array(
1858 return (sprintf('%s %s ("%s")', $fieldName, $operator, implode('", "', $escapedCriteria)));
1864 return(sprintf('%s %s "%s"', $fieldName, $operator, CRM_Core_DAO
::escapeString($criteria)));