From 2f00884e792f3c9a8fb4c2ea992b516fcd8e08a9 Mon Sep 17 00:00:00 2001 From: Hamish Friedlander Date: Wed, 29 Aug 2012 14:30:10 +1200 Subject: [PATCH] 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 --- core/ClassInfo.php | 6 ++++-- model/DataObject.php | 47 ++++++++++++++++++++++++++++++++++++-------- model/Versioned.php | 2 +- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/core/ClassInfo.php b/core/ClassInfo.php index 7aa17fa56..9c0f845fc 100644 --- a/core/ClassInfo.php +++ b/core/ClassInfo.php @@ -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; } /** diff --git a/model/DataObject.php b/model/DataObject.php index d7f5db288..d9320ae3e 100644 --- a/model/DataObject.php +++ b/model/DataObject.php @@ -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' ), @@ -443,6 +446,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. @@ -985,17 +999,34 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity $firstWrite = false; $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()) { - // 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); - return false; + 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 $writeException; + return false; + } + $this->onBeforeWrite(); if($this->brokenOnWrite) { user_error("$this->class has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR); diff --git a/model/Versioned.php b/model/Versioned.php index 25da1a7c4..b8efd4d87 100644 --- a/model/Versioned.php +++ b/model/Versioned.php @@ -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();