Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
  • Loading branch information...
commit 2f00884e792f3c9a8fb4c2ea992b516fcd8e08a9 1 parent cc2e250
@hafriedlander hafriedlander authored
Showing with 44 additions and 11 deletions.
  1. +4 −2 core/ClassInfo.php
  2. +39 −8 model/DataObject.php
  3. +1 −1  model/Versioned.php
View
6 core/ClassInfo.php
@@ -58,8 +58,10 @@ static function reset_db_cache() {
* 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
47 model/DataObject.php
@@ -181,9 +181,12 @@ static function set_validation_enabled($enable) {
*/
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 @@ private function duplicateRelations($sourceObject, $destinationObject, $name) {
}
}
}
+
+ 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 @@ public function write($showDebug = false, $forceInsert = false, $forceWrite = fa
$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);
View
2  model/Versioned.php
@@ -1103,7 +1103,7 @@ function __construct($record) {
$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();
Please sign in to comment.
Something went wrong with that request. Please try again.