Skip to content
This repository
Browse code

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
Hamish Friedlander authored August 29, 2012
6  core/ClassInfo.php
@@ -58,8 +58,10 @@ static function reset_db_cache() {
58 58
 	 * Returns the manifest of all classes which are present in the database.
59 59
 	 * @param string $class Class name to check enum values for ClassName field
60 60
 	 */
61  
-	static function getValidSubClasses($class = 'SiteTree') {
62  
-		return DB::getConn()->enumValuesForField($class, 'ClassName');
  61
+	static function getValidSubClasses($class = 'SiteTree', $includeUnbacked = false) {
  62
+		$classes = DB::getConn()->enumValuesForField($class, 'ClassName');
  63
+		if (!$includeUnbacked) $classes = array_filter($classes, array('ClassInfo', 'exists'));
  64
+		return $classes;
63 65
 	}
64 66
 
65 67
 	/**
47  model/DataObject.php
@@ -181,9 +181,12 @@ static function set_validation_enabled($enable) {
181 181
 	 */
182 182
 	public static function database_fields($class) {
183 183
 		if(get_parent_class($class) == 'DataObject') {
  184
+			$db = DB::getConn();
  185
+			$existing = $db->hasField($class, 'ClassName') ? $db->query("SELECT DISTINCT \"ClassName\" FROM \"$class\"")->column() : array();
  186
+
184 187
 			return array_merge (
185 188
 				array (
186  
-					'ClassName'  => "Enum('" . implode(', ', ClassInfo::subclassesFor($class)) . "')",
  189
+					'ClassName'  => "Enum('" . implode(', ', array_unique(array_merge($existing, ClassInfo::subclassesFor($class)))) . "')",
187 190
 					'Created'    => 'SS_Datetime',
188 191
 					'LastEdited' => 'SS_Datetime'
189 192
 				),
@@ -443,6 +446,17 @@ private function duplicateRelations($sourceObject, $destinationObject, $name) {
443 446
 			}
444 447
 		}
445 448
 	}
  449
+
  450
+	function getObsoleteClassName() {
  451
+		$className = $this->getField("ClassName");
  452
+		if (!ClassInfo::exists($className)) return $className;
  453
+	}
  454
+
  455
+	function getClassName() {
  456
+		$className = $this->getField("ClassName");
  457
+		if (!ClassInfo::exists($className)) return get_class($this);
  458
+		return $className;
  459
+	}
446 460
 	
447 461
 	/**
448 462
 	 * Set the ClassName attribute. {@link $class} is also updated.
@@ -985,17 +999,34 @@ public function write($showDebug = false, $forceInsert = false, $forceWrite = fa
985 999
 		$firstWrite = false;
986 1000
 		$this->brokenOnWrite = true;
987 1001
 		$isNewRecord = false;
988  
-		
989  
-		if(self::get_validation_enabled()) {
  1002
+
  1003
+		$writeException = null;
  1004
+
  1005
+		if ($this->ObsoleteClassName) {
  1006
+			$writeException = new ValidationException(
  1007
+				"Object is of class '{$this->ObsoleteClassName}' which doesn't exist - ".
  1008
+				"you need to change the ClassName before you can write it",
  1009
+				E_USER_WARNING
  1010
+			);
  1011
+		}
  1012
+		else if(self::get_validation_enabled()) {
990 1013
 			$valid = $this->validate();
991  
-			if(!$valid->valid()) {
992  
-				// Used by DODs to clean up after themselves, eg, Versioned
993  
-				$this->extend('onAfterSkippedWrite');
994  
-				throw new ValidationException($valid, "Validation error writing a $this->class object: " . $valid->message() . ".  Object not written.", E_USER_WARNING);
995  
-				return false;
  1014
+			if (!$valid->valid()) {
  1015
+				$writeException = new ValidationException(
  1016
+					$valid,
  1017
+					"Validation error writing a $this->class object: " . $valid->message() . ".  Object not written.",
  1018
+					E_USER_WARNING
  1019
+				);
996 1020
 			}
997 1021
 		}
998 1022
 
  1023
+		if($writeException) {
  1024
+			// Used by DODs to clean up after themselves, eg, Versioned
  1025
+			$this->extend('onAfterSkippedWrite');
  1026
+			throw $writeException;
  1027
+			return false;
  1028
+		}
  1029
+
999 1030
 		$this->onBeforeWrite();
1000 1031
 		if($this->brokenOnWrite) {
1001 1032
 			user_error("$this->class has a broken onBeforeWrite() function.  Make sure that you call parent::onBeforeWrite().", E_USER_ERROR);
2  model/Versioned.php
@@ -1103,7 +1103,7 @@ function __construct($record) {
1103 1103
 		$record['ID'] = $record['RecordID'];
1104 1104
 		$className = $record['ClassName'];
1105 1105
 		
1106  
-		$this->object = new $className($record);
  1106
+		$this->object = ClassInfo::exists($className) ? new $className($record) : new DataObject($record);
1107 1107
 		$this->failover = $this->object;
1108 1108
 		
1109 1109
 		parent::__construct();

0 notes on commit 2f00884

Please sign in to comment.
Something went wrong with that request. Please try again.