/**
  * 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) && singleton($candidateClass)->hasOwnTableDatabaseField($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) . '"';
     }
     //Cannot use ClassName.PropertyName in OrientDB queries
     return "{$this->name}";
 }
 /**
  * @see DataExtension::augmentSQL()
  */
 public function augmentSQL(SQLQuery &$query)
 {
     $select = $query->getSelect();
     if (empty($select) || $query->getDelete() || in_array("COUNT(*)", $select) || in_array("count(*)", $select)) {
         return;
     }
     if (!isset(self::$sortTables[$this->owner->class])) {
         $classes = array_reverse(ClassInfo::dataClassesFor($this->owner->class));
         $class = null;
         foreach ($classes as $cls) {
             if (DataObject::has_own_table($cls) && ($fields = DataObject::database_fields($cls)) && isset($fields['SortOrder'])) {
                 $class = $cls;
                 break;
             }
         }
         self::$sortTables[$this->owner->class] = $class;
     } else {
         $class = self::$sortTables[$this->owner->class];
     }
     if ($class) {
         $query->addOrderBy("\"{$class}\".\"SortOrder\" " . self::$sort_dir);
     } else {
         $query->addOrderBy("\"SortOrder\" " . self::$sort_dir);
     }
 }
 /**
  * Get all database fields to translate
  *
  * @param string $class Class name
  * @return array List of translated fields
  */
 public function getTranslatedFields($class)
 {
     if (isset($this->translatedFields[$class])) {
         return $this->translatedFields[$class];
     }
     $fields = array();
     $hierarchy = ClassInfo::ancestry($class);
     foreach ($hierarchy as $class) {
         // Skip classes without tables
         if (!DataObject::has_own_table($class)) {
             continue;
         }
         // Check translated fields for this class
         $translatedFields = FluentExtension::translated_fields_for($class);
         if (empty($translatedFields)) {
             continue;
         }
         // Save fields
         $fields = array_merge($fields, array_keys($translatedFields));
     }
     $this->translatedFields[$class] = $fields;
     return $fields;
 }
 /**
  * Get all the classes involved in a DataObject hierarchy - both super and optionally subclasses
  *
  * @static
  * @param String $class - The class to query
  * @param bool $includeSubclasses - True to return subclasses as well as super classes
  * @param bool $dataOnly - True to only return classes that have tables
  * @return Array - Integer keys, String values as classes sorted by depth (most super first)
  */
 static function hierarchy($class, $includeSubclasses = true, $dataOnly = false)
 {
     $key = "{$class}!" . ($includeSubclasses ? 'sc' : 'an') . '!' . ($dataOnly ? 'do' : 'al');
     if (!isset(self::$hierarchy[$key])) {
         $classes = array_values(ClassInfo::ancestry($class));
         if ($includeSubclasses) {
             $classes = array_unique(array_merge($classes, array_values(ClassInfo::subclassesFor($class))));
         }
         $idx = array_search('DataObject', $classes);
         if ($idx !== false) {
             array_splice($classes, 0, $idx + 1);
         }
         if ($dataOnly) {
             foreach ($classes as $i => $class) {
                 if (!DataObject::has_own_table($class)) {
                     unset($classes[$i]);
                 }
             }
         }
         self::$hierarchy[$key] = $classes;
     }
     return self::$hierarchy[$key];
 }
 /**
  * Get all database tables in the class ancestry and their respective
  * translatable fields
  *
  * @return array
  */
 protected function getTranslatedTables()
 {
     $includedTables = array();
     foreach ($this->owner->getClassAncestry() as $class) {
         // Skip classes without tables
         if (!DataObject::has_own_table($class)) {
             continue;
         }
         // Check translated fields for this class
         $translatedFields = self::translated_fields_for($class);
         if (empty($translatedFields)) {
             continue;
         }
         // Mark this table as translatable
         $includedTables[$class] = array_keys($translatedFields);
     }
     return $includedTables;
 }
Example #6
0
 /**
  * Normalizes the field name to table mapping.
  * 
  * @return string
  */
 public function getDbName()
 {
     // Special handler for "NULL" relations
     if ($this->name == "NULL") {
         return $this->name;
     }
     // SRM: 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) && singleton($candidateClass)->hasOwnTableDatabaseField($this->name)) {
             break;
         }
         $candidateClass = get_parent_class($candidateClass);
     }
     if ($candidateClass == 'DataObject') {
         user_error("Couldn't find field {$this->name} in any of {$this->model}'s tables.", E_USER_ERROR);
     }
     return "\"{$candidateClass}\".\"{$this->name}\"";
 }
 /**
  * Determine if a table is supporting the Versioned extensions (e.g. $table_versions does exists)
  *
  * @param string $table Table name
  * @return boolean
  */
 function canBeVersioned($table)
 {
     return ClassInfo::exists($table) && ClassInfo::is_subclass_of($table, 'DataObject') && DataObject::has_own_table($table);
 }
 /**
  * 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;
 }
	/**
	 * Construct a new cleaner for the given DataObject class.
	 * 
	 * @param string $class
	 */
	public function __construct($class) {
		set_time_limit(self::get_time_limit());
		
		$this->className = $class;
		$this->dataObject = singleton($this->className);
		$this->baseClassName = ClassInfo::baseDataClass($this->className);
		$this->tableAncestors = ClassInfo::ancestry($this->className, true);
		$this->allAncestors = ClassInfo::ancestry($this->className, false);
		// Remove Object, ViewableData, DataObject
		$this->allAncestors = array_slice($this->allAncestors, 3, count($this->allAncestors) - 3, true);
		
		JanitorDebug::message("Class: {$this->className}", 'h3');
		JanitorDebug::message('Table ancestors: ' . var_export($this->tableAncestors, true), 'pre');
		JanitorDebug::message('All ancestors: ' . var_export($this->allAncestors, true), 'pre');
		
		// Select the table that we will be working with.
		$this->table = array_reverse($this->tableAncestors, false);
		$this->table = DataObject::has_own_table($this->className)? $this->className: array_shift($this->table);
		
		JanitorDebug::message("Selected table: {$this->table}", 'h4');
	}
 /**
  * Add a DataObject subclass whose instances should be included in this index
  *
  * Can only be called when addFulltextField, addFilterField, addSortField and addAllFulltextFields have not
  * yet been called for this index instance
  *
  * @throws Exception
  * @param String $class - The class to include
  * @param array $options - TODO: Remove
  */
 public function addClass($class, $options = array())
 {
     if ($this->fulltextFields || $this->filterFields || $this->sortFields) {
         throw new Exception('Can\'t add class to Index after fields have already been added');
     }
     if (!DataObject::has_own_table($class)) {
         throw new InvalidArgumentException('Can\'t add classes which don\'t have data tables (no $db or $has_one set on the class)');
     }
     $options = array_merge(array('include_children' => true), $options);
     $this->classes[$class] = $options;
 }
 /**
  * Rebuild all vfi fields.
  */
 public function rebuildVFI($field = '')
 {
     if ($field) {
         $this->isRebuilding = true;
         $spec = $this->getVFISpec($field);
         $fn = $this->getVFIFieldName($field);
         $val = $this->getVFI($field, true);
         if ($spec['Type'] == self::TYPE_LIST) {
             if (is_object($val)) {
                 $val = $val->toArray();
             }
             // this would be an ArrayList or DataList
             if (!is_array($val)) {
                 $val = array($val);
             }
             // this would be a scalar value
             $val = self::encode_list($val);
         } else {
             if (is_array($val)) {
                 $val = (string) $val[0];
             }
             // if they give us an array, just take the first value
             if (is_object($val)) {
                 $val = (string) $val->first();
             }
             // if a SS_List, take the first as well
         }
         if (Config::inst()->get('VirtualFieldIndex', 'fast_writes_enabled')) {
             // NOTE: this is usually going to be bad practice, but if you
             // have a lot of products and a lot of on...Write handlers that
             // can get tedious really quick. This is just here as an option.
             $table = '';
             foreach ($this->owner->getClassAncestry() as $ancestor) {
                 if (DataObject::has_own_table($ancestor)) {
                     $sing = singleton($ancestor);
                     if ($sing->hasOwnTableDatabaseField($fn)) {
                         $table = $ancestor;
                         break;
                     }
                 }
             }
             if (!empty($table)) {
                 DB::query($sql = sprintf("UPDATE %s SET %s = '%s' WHERE ID = '%d'", $table, $fn, Convert::raw2sql($val), $this->owner->ID));
                 DB::query(sprintf("UPDATE %s_Live SET %s = '%s' WHERE ID = '%d'", $table, $fn, Convert::raw2sql($val), $this->owner->ID));
                 $this->owner->setField($fn, $val);
             } else {
                 // if we couldn't figure out the right table, fall back to the old fashioned way
                 $this->owner->setField($fn, $val);
                 $this->owner->write();
             }
         } else {
             $this->owner->setField($fn, $val);
             $this->owner->write();
         }
         $this->isRebuilding = false;
     } else {
         // rebuild all fields if they didn't specify
         foreach ($this->getVFISpec() as $field => $spec) {
             $this->rebuildVFI($field);
         }
     }
 }
Example #12
0
 /**
  * @todo Improve documentation
  */
 static function ancestry($class, $onlyWithTables = false)
 {
     global $_ALL_CLASSES;
     if (is_object($class)) {
         $class = $class->class;
     } else {
         if (!is_string($class)) {
             user_error("Bad class value " . var_export($class, true) . " passed to ClassInfo::ancestry()", E_USER_WARNING);
         }
     }
     $items = $_ALL_CLASSES['parents'][$class];
     $items[$class] = $class;
     if ($onlyWithTables) {
         foreach ($items as $item) {
             if (!DataObject::has_own_table($item)) {
                 unset($items[$item]);
             }
         }
     }
     return $items;
 }
 /**
  * Remove invalid records from tables - that is, records that don't have
  * corresponding records in their parent class tables.
  */
 public function cleanup()
 {
     $allClasses = get_declared_classes();
     foreach ($allClasses as $class) {
         if (get_parent_class($class) == 'DataObject') {
             $baseClasses[] = $class;
         }
     }
     foreach ($baseClasses as $baseClass) {
         // Get data classes
         $subclasses = ClassInfo::subclassesFor($baseClass);
         unset($subclasses[0]);
         foreach ($subclasses as $k => $subclass) {
             if (DataObject::has_own_table($subclass)) {
                 unset($subclasses[$k]);
             }
         }
         if ($subclasses) {
             $records = DB::query("SELECT * FROM \"{$baseClass}\"");
             foreach ($subclasses as $subclass) {
                 $recordExists[$subclass] = DB::query("SELECT \"ID\" FROM \"{$subclass}\"")->keyedColumn();
             }
             foreach ($records as $record) {
                 foreach ($subclasses as $subclass) {
                     $id = $record['ID'];
                     if ($record['ClassName'] != $subclass && !is_subclass_of($record['ClassName'], $subclass) && isset($recordExists[$subclass][$id])) {
                         $sql = "DELETE FROM \"{$subclass}\" WHERE \"ID\" = {$record['ID']}";
                         echo "<li>{$sql}";
                         DB::query($sql);
                     }
                 }
             }
         }
     }
 }
Example #14
0
 /**
  * Returns the passed class name along with all its parent class names in an
  * array, sorted with the root class first.
  *
  * @param  string $class
  * @param  bool $tablesOnly Only return classes that have a table in the db.
  * @return array
  */
 public static function ancestry($class, $tablesOnly = false)
 {
     $ancestry = array();
     if (is_object($class)) {
         $class = get_class($class);
     } elseif (!is_string($class)) {
         throw new Exception(sprintf('Invalid class value %s, must be an object or string', var_export($class, true)));
     }
     do {
         if (!$tablesOnly || DataObject::has_own_table($class)) {
             $ancestry[$class] = $class;
         }
     } while ($class = get_parent_class($class));
     return array_reverse($ancestry);
 }
Example #15
0
 /**
  * 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 || !is_subclass_of($candidateClass, 'DataObject')) {
         return null;
     }
     //normalise class name
     $candidateClass = self::class_name($candidateClass);
     $exists = self::exists($candidateClass);
     while ($candidateClass && $candidateClass != 'DataObject' && $exists) {
         if (DataObject::has_own_table($candidateClass)) {
             $inst = singleton($candidateClass);
             if ($inst->hasOwnTableDatabaseField($fieldName)) {
                 break;
             }
         }
         $candidateClass = get_parent_class($candidateClass);
         $exists = $candidateClass && self::exists($candidateClass);
     }
     if (!$candidateClass || !$exists) {
         return null;
     }
     return $candidateClass;
 }
 /**
  * @param $className
  * @param $ids
  * @param string $postfix
  */
 private function deleteTablePostfix($className, $ids, $postfix = '')
 {
     if (empty($ids)) {
         return;
     }
     if ($postfix === 'Stage') {
         $postfix = '';
     }
     $singleton = singleton($className);
     $ancestry = array_reverse(array_filter($singleton->getClassAncestry(), function ($class) {
         return DataObject::has_own_table($class);
     }));
     $field = DBField::create_field('Int', null, 'ID');
     $ids = '(' . implode(', ', array_map(function ($id) use($field) {
         $id = $id instanceof \DataObject ? $id->ID : $id;
         return $field->prepValueForDB($id);
     }, $ids)) . ')';
     foreach ($ancestry as $class) {
         $table = $class . ($postfix ? '_' . $postfix : '');
         $sql = "DELETE FROM `{$table}` WHERE ID IN {$ids}";
         DB::query($sql);
     }
 }
 /**
  * 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) {
         return null;
     }
     $exists = class_exists($candidateClass);
     while ($candidateClass && $candidateClass != 'DataObject' && $exists) {
         if (DataObject::has_own_table($candidateClass)) {
             $inst = singleton($candidateClass);
             if ($inst->hasOwnTableDatabaseField($fieldName)) {
                 break;
             }
         }
         $candidateClass = get_parent_class($candidateClass);
         $exists = class_exists($candidateClass);
     }
     if (!$candidateClass || !$exists) {
         return null;
     }
     return $candidateClass;
 }
 public function TestHasOwnTable()
 {
     /* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
     $this->assertTrue(DataObject::has_own_table("DataObjectTest_Player"));
     $this->assertTrue(DataObject::has_own_table("DataObjectTest_Team"));
     $this->assertTrue(DataObject::has_own_table("DataObjectTest_Fixture"));
     /* Root DataObject that always have a table, even if they lack both $db and $has_one */
     $this->assertTrue(DataObject::has_own_table("DataObjectTest_FieldlessTable"));
     /* Subclasses without $db or $has_one don't have a table */
     $this->assertFalse(DataObject::has_own_table("DataObjectTest_FieldlessSubTable"));
     /* Return false if you don't pass it a subclass of DataObject */
     $this->assertFalse(DataObject::has_own_table("DataObject"));
     $this->assertFalse(DataObject::has_own_table("ViewableData"));
     $this->assertFalse(DataObject::has_own_table("ThisIsntADataObject"));
 }
Example #19
0
 /**
  * Returns the passed class name along with all its parent class names in an
  * array, sorted with the root class first.
  *
  * @param  string $class
  * @param  bool $tablesOnly Only return classes that have a table in the db.
  * @return array
  */
 public static function ancestry($class, $tablesOnly = false)
 {
     if (!is_string($class)) {
         $class = get_class($class);
     }
     $cacheKey = $class . '_' . (string) $tablesOnly;
     $parent = $class;
     if (!isset(self::$_cache_ancestry[$cacheKey])) {
         $ancestry = array();
         do {
             if (!$tablesOnly || DataObject::has_own_table($parent)) {
                 $ancestry[$parent] = $parent;
             }
         } while ($parent = get_parent_class($parent));
         self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry);
     }
     return self::$_cache_ancestry[$cacheKey];
 }