Merge pull request #911 from agh1/membership-dash-counts-new
[civicrm-core.git] / CRM / Core / DAO.php
index 15fa9cfd6ae56d6774fe5da20bd3575fa47181dd..697a671fa857d0c3bdbec4ea4e3e55c27f34555f 100644 (file)
@@ -55,7 +55,10 @@ class CRM_Core_DAO extends DB_DataObject {
   // special value for mail bulk inserts to avoid
   // potential duplication, assuming a smaller number reduces number of queries
   // by some factor, so some tradeoff. CRM-8678
-  BULK_MAIL_INSERT_COUNT = 10;
+  BULK_MAIL_INSERT_COUNT = 10,
+  QUERY_FORMAT_WILDCARD = 1,
+  QUERY_FORMAT_NO_QUOTES = 2;
+
   /*
    * Define entities that shouldn't be created or deleted when creating/ deleting
    *  test objects - this prevents world regions, countries etc from being added / deleted
@@ -190,20 +193,7 @@ class CRM_Core_DAO extends DB_DataObject {
    * @access protected
    */
   function initialize() {
-    $links = $this->links();
-    if (empty($links)) {
-      return;
-    }
-
     $this->_connect();
-
-    if (!isset($GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database])) {
-      $GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database] = array();
-    }
-
-    if (!array_key_exists($this->__table, $GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database])) {
-      $GLOBALS['_DB_DATAOBJECT']['LINKS'][$this->_database][$this->__table] = $links;
-    }
   }
 
   /**
@@ -240,12 +230,13 @@ class CRM_Core_DAO extends DB_DataObject {
   /**
    * returns list of FK relationships
    *
+   * @static
    * @access public
    *
-   * @return array
+   * @return array of CRM_Core_EntityReference
    */
-  function links() {
-    return NULL;
+  static function getReferenceColumns() {
+    return array();
   }
 
   /**
@@ -273,9 +264,6 @@ class CRM_Core_DAO extends DB_DataObject {
       }
     }
 
-    // set the links
-    $this->links();
-
     return $table;
   }
 
@@ -589,17 +577,18 @@ LIKE %1
     static $show = array();
 
     if (!array_key_exists($tableName, $show)) {
-      $query = "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = %1";
-      $params = array( 1 => array($tableName, 'String'));
-      $dao = CRM_Core_DAO::executeQuery($query, $params);
+      $query = "SHOW CREATE TABLE $tableName";
+      $dao = CRM_Core_DAO::executeQuery($query);
 
-      while ($dao->fetch()) {
-        $show[$tableName][] = $dao->CONSTRAINT_NAME;
+      if (!$dao->fetch()) {
+        CRM_Core_Error::fatal();
       }
+
       $dao->free();
+      $show[$tableName] = $dao->Create_Table;
     }
 
-    return in_array($constraint, $show[$tableName]) ? TRUE : FALSE;
+    return preg_match("/\b$constraint\b/i", $show[$tableName]) ? TRUE : FALSE;
   }
 
   /**
@@ -979,10 +968,17 @@ FROM   civicrm_domain
             $item[1] == 'Memo' ||
             $item[1] == 'Link'
           ) {
-            if (isset($item[2]) &&
-              $item[2]
-            ) {
-              $item[0] = "'%{$item[0]}%'";
+            // Support class constants stipulating wildcard characters and/or
+            // non-quoting of strings. Also support legacy code which may be
+            // passing in TRUE or 1 for $item[2], which used to indicate the
+            // use of wildcard characters.
+            if (!empty($item[2])) {
+              if ($item[2] & CRM_Core_DAO::QUERY_FORMAT_WILDCARD || $item[2] === TRUE) {
+                $item[0] = "'%{$item[0]}%'";
+              }
+              elseif (!($item[2] & CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES)) {
+                $item[0] = "'{$item[0]}'";
+              }
             }
             else {
               $item[0] = "'{$item[0]}'";
@@ -1279,12 +1275,10 @@ SELECT contact_id
       'CRM_Core_DAO_Domain',
     );
 
-    require_once (str_replace('_', DIRECTORY_SEPARATOR, $daoName) . ".php");
-
     for ($i = 0; $i < $numObjects; ++$i) {
 
       ++$counter;
-      $object   = new $daoName ( );
+      $object = new $daoName();
 
       $fields = &$object->fields();
       foreach ($fields as $name => $value) {
@@ -1327,21 +1321,13 @@ SELECT contact_id
 
             continue;
           }
-          $constant = CRM_Utils_Array::value('pseudoconstant', $value);
-          if (!empty($constant)) {
-            $constantValues = CRM_Utils_PseudoConstant::getConstant($constant['name']);
-            if (!empty($constantValues)) {
-              $constantOptions = array_keys($constantValues);
-              $object->$dbName = $constantOptions[0];
-            }
-            continue;
-          }
-          $enum = CRM_Utils_Array::value('enumValues', $value);
-          if (!empty($enum)) {
-            $options = explode(',', $enum);
-            $object->$dbName = $options[0];
+          // Pick an option value if needed
+          $options = $daoName::buildOptions($dbName);
+          if ($options) {
+            $object->$dbName = key($options);
             continue;
           }
+
           switch ($value['type']) {
             case CRM_Utils_Type::T_INT:
             case CRM_Utils_Type::T_FLOAT:
@@ -1686,30 +1672,70 @@ SELECT contact_id
   }
 
   /**
-   * Check the tables sent in, to see if there are any tables where there is a value for
-   * a column
+   * Find all records which refer to this entity.
+   *
+   * @return array of objects referencing this
+   */
+  function findReferences() {
+    $links = self::getReferencesToTable(static::getTableName());
+
+    $occurrences = array();
+    foreach ($links as $refSpec) {
+      $refColumn = $refSpec->getReferenceKey();
+      $targetColumn = $refSpec->getTargetKey();
+      $params = array(1 => array($this->$targetColumn, 'String'));
+      $sql = <<<EOS
+SELECT id
+FROM {$refSpec->getReferenceTable()}
+WHERE {$refColumn} = %1
+EOS;
+      if ($refSpec->isGeneric()) {
+        $params[2] = array(static::getTableName(), 'String');
+        $sql .= <<<EOS
+    AND {$refSpec->getTypeColumn()} = %2
+EOS;
+      }
+      $daoName = CRM_Core_DAO_AllCoreTables::getClassForTable($refSpec->getReferenceTable());
+      $result = self::executeQuery($sql, $params, TRUE, $daoName);
+      while ($result->fetch()) {
+        $obj = new $daoName();
+        $obj->id = $result->id;
+        $occurrences[] = $obj;
+      }
+    }
+
+    return $occurrences;
+  }
+
+  /**
+   * List all tables which have hard foreign keys to this table.
    *
-   * This is typically used when we want to delete a row, but want to avoid the FK errors
-   * that it might cause due to this being a required FK
+   * For now, this returns a description of every entity_id/entity_table
+   * reference.
+   * TODO: filter dynamic entity references on the $tableName, based on
+   * schema metadata in dynamicForeignKey which enumerates a restricted
+   * set of possible entity_table's.
    *
-   * @param array an array of values (tableName, columnName)
-   * @param array the parameter array with the value and type
-   * @param array (reference) the tables which had an entry for this value
+   * @param string $tableName table referred to
    *
-   * @return boolean true if no value exists in all the tables
-   * @static
+   * @return array structure of table and column, listing every table with a
+   * foreign key reference to $tableName, and the column where the key appears.
    */
-  public static function doesValueExistInTable(&$tables, $params, &$errors) {
-    $errors = array();
-    foreach ($tables as $table) {
-      $sql = "SELECT count(*) FROM {$table['table']} WHERE {$table['column']} = %1";
-      $count = self::singleValueQuery($sql, $params);
-      if ($count > 0) {
-        $errors[$table['table']] = $count;
+  static function getReferencesToTable($tableName) {
+    $refsFound = array();
+    foreach (CRM_Core_DAO_AllCoreTables::getClasses() as $daoClassName) {
+      $links = $daoClassName::getReferenceColumns();
+      $daoTableName = $daoClassName::getTableName();
+
+      foreach ($links as $refSpec) {
+        if ($refSpec->getTargetTable() === $tableName
+              or $refSpec->isGeneric()
+        ) {
+          $refsFound[] = $refSpec;
+        }
       }
     }
-
-    return (empty($errors)) ? FALSE : TRUE;
+    return $refsFound;
   }
 
   /**
@@ -1731,5 +1757,20 @@ SELECT contact_id
       return $default;
     }
   }
+
+  /**
+   * Get options for the called BAO object's field.
+   * This function can be overridden by each BAO to add more logic related to context.
+   * The overriding function will generally call the lower-level CRM_Core_PseudoConstant::get
+   *
+   * @param String $fieldName
+   * @param String $context: e.g. "search" "edit" "create" "view"
+   * @param Array  $props: whatever is known about this bao object
+   */
+  public static function buildOptions($fieldName, $context = NULL, $props = array()) {
+    // If a given bao does not override this function
+    $baoName = get_called_class();
+    return CRM_Core_PseudoConstant::get($baoName, $fieldName);
+  }
 }