From 953153500d490f5b5abf7283c34242c3b22a855a Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Fri, 18 May 2018 13:04:36 +1200 Subject: [PATCH 1/3] FIX Polymorphic relationship class columns have obsolete class names remapped --- src/ORM/DatabaseAdmin.php | 112 ++++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 29 deletions(-) diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php index c556c7bb0..cedcdec66 100644 --- a/src/ORM/DatabaseAdmin.php +++ b/src/ORM/DatabaseAdmin.php @@ -7,9 +7,11 @@ use SilverStripe\Control\Controller; 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; @@ -319,35 +321,12 @@ class DatabaseAdmin extends Controller } // 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 "
  • Correcting $badRecordCount obsolete classname values for $newClassName
  • \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); } } } @@ -390,6 +369,81 @@ class DatabaseAdmin extends Controller 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 "
  • Correcting {$badRecordCount} obsolete {$fieldName} values for {$newClassName}
  • \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. From d651d0fbfcababeaf317b27cb00b4f33b9d99eab Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Tue, 28 Aug 2018 14:15:02 +1200 Subject: [PATCH 2/3] FIX Use base class (not remapping target class) when looking up whether object is versioned --- src/ORM/DatabaseAdmin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php index cedcdec66..0a71020b9 100644 --- a/src/ORM/DatabaseAdmin.php +++ b/src/ORM/DatabaseAdmin.php @@ -407,7 +407,7 @@ class DatabaseAdmin extends Controller // Remap versioned table class name values as well /** @var Versioned|DataObject $class */ - $class = DataObject::singleton($newClassName); + $class = DataObject::singleton($dataClass); if ($class->hasExtension(Versioned::class)) { if ($class->hasStages()) { $updateQueries[] = sprintf($updateQuery, '_Live'); From b922c0d7327b5d0222dd280afcb64f83a09ea859 Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Mon, 3 Sep 2018 08:59:37 +0200 Subject: [PATCH 3/3] FIX Check scheme is truthy before setting it to the request --- src/Control/CLIRequestBuilder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Control/CLIRequestBuilder.php b/src/Control/CLIRequestBuilder.php index 9ad888355..440f2218e 100644 --- a/src/Control/CLIRequestBuilder.php +++ b/src/Control/CLIRequestBuilder.php @@ -79,7 +79,9 @@ class CLIRequestBuilder extends HTTPRequestBuilder $request = parent::createFromVariables($variables, $input); // unset scheme so that SS_BASE_URL can provide `is_https` information if required $scheme = parse_url(Environment::getEnv('SS_BASE_URL'), PHP_URL_SCHEME); - $request->setScheme($scheme); + if ($scheme) { + $request->setScheme($scheme); + } return $request; }