Skip to content

Commit

Permalink
FIX Polymorphic relationship class columns have obsolete class names …
Browse files Browse the repository at this point in the history
…remapped
  • Loading branch information
robbieaverill committed Aug 14, 2018
1 parent 80bf0fc commit 9531535
Showing 1 changed file with 83 additions and 29 deletions.
112 changes: 83 additions & 29 deletions src/ORM/DatabaseAdmin.php
Expand Up @@ -7,9 +7,11 @@
use SilverStripe\Control\Director;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Environment;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Dev\DevelopmentAdmin;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\FieldType\DBClassName;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
use SilverStripe\Versioned\Versioned;
Expand Down Expand Up @@ -319,35 +321,12 @@ public function doBuild($quiet = false, $populate = true, $testMode = false)
}

// Remap obsolete class names
$schema = DataObject::getSchema();
foreach ($this->config()->classname_value_remapping as $oldClassName => $newClassName) {
$baseDataClass = $schema->baseDataClass($newClassName);
$badRecordCount = DataObject::get($baseDataClass)
->filter(["ClassName" => $oldClassName ])
->count();
if ($badRecordCount > 0) {
if (Director::is_cli()) {
echo " * Correcting $badRecordCount obsolete classname values for $newClassName\n";
} else {
echo "<li>Correcting $badRecordCount obsolete classname values for $newClassName</li>\n";
}
$table = $schema->baseDataTable($baseDataClass);

$updateQuery = "UPDATE \"$table%s\" SET \"ClassName\" = ? WHERE \"ClassName\" = ?";
$updateQueries = [sprintf($updateQuery, '')];

// Remap versioned table ClassName values as well
/** @var Versioned|DataObject $class */
$class = DataObject::singleton($newClassName);
if ($class->hasExtension(Versioned::class)) {
if ($class->hasStages()) {
$updateQueries[] = sprintf($updateQuery, '_Live');
}
$updateQueries[] = sprintf($updateQuery, '_Versions');
}

foreach ($updateQueries as $query) {
DB::prepared_query($query, [$newClassName, $oldClassName]);
$remappingConfig = $this->config()->get('classname_value_remapping');
$remappingFields = $this->getClassNameRemappingFields();
foreach ($remappingFields as $className => $fieldNames) {
foreach ($fieldNames as $fieldName) {
foreach ($remappingConfig as $oldClassName => $newClassName) {
$this->updateLegacyClassNames($className, $fieldName, $oldClassName, $newClassName);
}
}
}
Expand Down Expand Up @@ -390,6 +369,81 @@ public function doBuild($quiet = false, $populate = true, $testMode = false)
ClassInfo::reset_db_cache();
}

/**
* Given a base data class, a field name and an old and new class name (value), look for obsolete ($oldClassName)
* values in the $dataClass's $fieldName column and replace it with $newClassName.
*
* @param string $dataClass The data class to look up
* @param string $fieldName The field name to look in for obsolete class names
* @param string $oldClassName The old class name
* @param string $newClassName The new class name
*/
protected function updateLegacyClassNames($dataClass, $fieldName, $oldClassName, $newClassName)
{
$schema = DataObject::getSchema();
// Check first to ensure that the class has the specified field to update
if (!$schema->databaseField($dataClass, $fieldName, false)) {
return;
}

// Load a list of any records that have obsolete class names
$badRecordCount = DataObject::get($dataClass)
->filter([$fieldName => $oldClassName])
->count();

if (!$badRecordCount) {
return;
}

if (Director::is_cli()) {
echo " * Correcting {$badRecordCount} obsolete {$fieldName} values for {$newClassName}\n";
} else {
echo "<li>Correcting {$badRecordCount} obsolete {$fieldName} values for {$newClassName}</li>\n";
}
$table = $schema->tableName($dataClass);

$updateQuery = "UPDATE \"{$table}%s\" SET \"{$fieldName}\" = ? WHERE \"{$fieldName}\" = ?";
$updateQueries = [sprintf($updateQuery, '')];

// Remap versioned table class name values as well
/** @var Versioned|DataObject $class */
$class = DataObject::singleton($newClassName);
if ($class->hasExtension(Versioned::class)) {
if ($class->hasStages()) {
$updateQueries[] = sprintf($updateQuery, '_Live');
}
$updateQueries[] = sprintf($updateQuery, '_Versions');
}

foreach ($updateQueries as $query) {
DB::prepared_query($query, [$newClassName, $oldClassName]);
}
}

/**
* Find all DBClassName fields on valid subclasses of DataObject that should be remapped. This includes
* `ClassName` fields as well as polymorphic class name fields.
*
* @return array[]
*/
protected function getClassNameRemappingFields()
{
$dataClasses = ClassInfo::getValidSubClasses(DataObject::class);
$schema = DataObject::getSchema();
$remapping = [];

foreach ($dataClasses as $className) {
$fieldSpecs = $schema->fieldSpecs($className);
foreach ($fieldSpecs as $fieldName => $fieldSpec) {
if (Injector::inst()->create($fieldSpec, 'Dummy') instanceof DBClassName) {
$remapping[$className][] = $fieldName;
}
}
}

return $remapping;
}

/**
* Remove invalid records from tables - that is, records that don't have
* corresponding records in their parent class tables.
Expand Down

0 comments on commit 9531535

Please sign in to comment.