/**
  * Returns the table name in the class hierarchy which contains a given
  * field column for a {@link DataObject}. If the field does not exist, this
  * will return null.
  *
  * @param string $candidateClass
  * @param string $fieldName
  *
  * @return string
  */
 public static function table_for_object_field($candidateClass, $fieldName)
 {
     if (!$candidateClass || !$fieldName || !class_exists($candidateClass) || !is_subclass_of($candidateClass, 'DataObject')) {
         return null;
     }
     //normalise class name
     $candidateClass = self::class_name($candidateClass);
     $exists = self::exists($candidateClass);
     // Short circuit for fixed fields
     $fixed = DataObject::config()->fixed_fields;
     if ($exists && isset($fixed[$fieldName])) {
         return self::baseDataClass($candidateClass);
     }
     // Find regular field
     while ($candidateClass && $candidateClass != 'DataObject' && $exists) {
         if (DataObject::has_own_table($candidateClass) && DataObject::has_own_table_database_field($candidateClass, $fieldName)) {
             break;
         }
         $candidateClass = get_parent_class($candidateClass);
         $exists = $candidateClass && self::exists($candidateClass);
     }
     if (!$candidateClass || !$exists) {
         return null;
     }
     return $candidateClass;
 }
 /**
  * Ensure that if a query has an order by clause, those columns are present in the select.
  *
  * @param SQLSelect $query
  * @return null
  */
 protected function ensureSelectContainsOrderbyColumns($query, $originalSelect = array())
 {
     $tableClasses = ClassInfo::dataClassesFor($this->dataClass);
     $baseClass = array_shift($tableClasses);
     if ($orderby = $query->getOrderBy()) {
         $newOrderby = array();
         foreach ($orderby as $k => $dir) {
             $newOrderby[$k] = $dir;
             // don't touch functions in the ORDER BY or public function calls
             // selected as fields
             if (strpos($k, '(') !== false) {
                 continue;
             }
             $col = str_replace('"', '', trim($k));
             $parts = explode('.', $col);
             // Pull through SortColumn references from the originalSelect variables
             if (preg_match('/_SortColumn/', $col)) {
                 if (isset($originalSelect[$col])) {
                     $query->selectField($originalSelect[$col], $col);
                 }
                 continue;
             }
             if (count($parts) == 1) {
                 if (DataObject::has_own_table_database_field($baseClass, $parts[0])) {
                     $qualCol = "\"{$baseClass}\".\"{$parts[0]}\"";
                 } else {
                     $qualCol = "\"{$parts['0']}\"";
                 }
                 // remove original sort
                 unset($newOrderby[$k]);
                 // add new columns sort
                 $newOrderby[$qualCol] = $dir;
                 // To-do: Remove this if block once SQLSelect::$select has been refactored to store getSelect()
                 // format internally; then this check can be part of selectField()
                 $selects = $query->getSelect();
                 if (!isset($selects[$col]) && !in_array($qualCol, $selects)) {
                     $query->selectField($qualCol);
                 }
             } else {
                 $qualCol = '"' . implode('"."', $parts) . '"';
                 // To-do: Remove this if block once SQLSelect::$select has been refactored to store getSelect()
                 // format internally; then this check can be part of selectField()
                 if (!in_array($qualCol, $query->getSelect())) {
                     $query->selectField($qualCol);
                 }
             }
         }
         $query->setOrderBy($newOrderby);
     }
 }
 /**
  * Normalizes the field name to table mapping.
  *
  * @return string
  */
 public function getDbName()
 {
     // Special handler for "NULL" relations
     if ($this->name == "NULL") {
         return $this->name;
     }
     // This code finds the table where the field named $this->name lives
     // Todo: move to somewhere more appropriate, such as DataMapper, the
     // magical class-to-be?
     $candidateClass = $this->model;
     while ($candidateClass != 'DataObject') {
         if (DataObject::has_own_table($candidateClass) && DataObject::has_own_table_database_field($candidateClass, $this->name)) {
             break;
         }
         $candidateClass = get_parent_class($candidateClass);
     }
     if ($candidateClass == 'DataObject') {
         // fallback to the provided name in the event of a joined column
         // name (as the candidate class doesn't check joined records)
         $parts = explode('.', $this->fullName);
         return '"' . implode('"."', $parts) . '"';
     }
     return "\"{$candidateClass}\".\"{$this->name}\"";
 }