FIX If ClassName read from DB doesnt exist, dont break

We know the subclass of a record by its ClassName value, but code changes
might have meant that class no longer exists. We used to just break,
but this patch overrides the apparent value of ClassName to be
one that exists in that situation
This commit is contained in:
Hamish Friedlander 2012-08-29 14:30:10 +12:00
parent cc2e250719
commit 2f00884e79
3 changed files with 44 additions and 11 deletions

View File

@ -58,8 +58,10 @@ class ClassInfo {
* Returns the manifest of all classes which are present in the database.
* @param string $class Class name to check enum values for ClassName field
*/
static function getValidSubClasses($class = 'SiteTree') {
return DB::getConn()->enumValuesForField($class, 'ClassName');
static function getValidSubClasses($class = 'SiteTree', $includeUnbacked = false) {
$classes = DB::getConn()->enumValuesForField($class, 'ClassName');
if (!$includeUnbacked) $classes = array_filter($classes, array('ClassInfo', 'exists'));
return $classes;
}
/**

View File

@ -181,9 +181,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*/
public static function database_fields($class) {
if(get_parent_class($class) == 'DataObject') {
$db = DB::getConn();
$existing = $db->hasField($class, 'ClassName') ? $db->query("SELECT DISTINCT \"ClassName\" FROM \"$class\"")->column() : array();
return array_merge (
array (
'ClassName' => "Enum('" . implode(', ', ClassInfo::subclassesFor($class)) . "')",
'ClassName' => "Enum('" . implode(', ', array_unique(array_merge($existing, ClassInfo::subclassesFor($class)))) . "')",
'Created' => 'SS_Datetime',
'LastEdited' => 'SS_Datetime'
),
@ -444,6 +447,17 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
}
function getObsoleteClassName() {
$className = $this->getField("ClassName");
if (!ClassInfo::exists($className)) return $className;
}
function getClassName() {
$className = $this->getField("ClassName");
if (!ClassInfo::exists($className)) return get_class($this);
return $className;
}
/**
* Set the ClassName attribute. {@link $class} is also updated.
* Warning: This will produce an inconsistent record, as the object
@ -986,15 +1000,32 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$this->brokenOnWrite = true;
$isNewRecord = false;
if(self::get_validation_enabled()) {
$writeException = null;
if ($this->ObsoleteClassName) {
$writeException = new ValidationException(
"Object is of class '{$this->ObsoleteClassName}' which doesn't exist - ".
"you need to change the ClassName before you can write it",
E_USER_WARNING
);
}
else if(self::get_validation_enabled()) {
$valid = $this->validate();
if (!$valid->valid()) {
$writeException = new ValidationException(
$valid,
"Validation error writing a $this->class object: " . $valid->message() . ". Object not written.",
E_USER_WARNING
);
}
}
if($writeException) {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
throw new ValidationException($valid, "Validation error writing a $this->class object: " . $valid->message() . ". Object not written.", E_USER_WARNING);
throw $writeException;
return false;
}
}
$this->onBeforeWrite();
if($this->brokenOnWrite) {

View File

@ -1103,7 +1103,7 @@ class Versioned_Version extends ViewableData {
$record['ID'] = $record['RecordID'];
$className = $record['ClassName'];
$this->object = new $className($record);
$this->object = ClassInfo::exists($className) ? new $className($record) : new DataObject($record);
$this->failover = $this->object;
parent::__construct();