diff --git a/docs/en/03_Upgrading/01_Upgrading_project.md b/docs/en/03_Upgrading/01_Upgrading_project.md index 0753674c1..bfd8c179c 100644 --- a/docs/en/03_Upgrading/01_Upgrading_project.md +++ b/docs/en/03_Upgrading/01_Upgrading_project.md @@ -1235,6 +1235,14 @@ has been added to assist in migration of existing files (see [file migration doc ./vendor/bin/sake dev/tasks/MigrateFileTask ``` +##### Rewriting asset references + +Your `img` and `a` tag references to your assets may now be pointing to a location in your assets folder that has been moved. There is a task available which will look through all your tables containing `HTMLText` and `HTMLVarchar` fields looking for broken references and then rewrite them to the new location of the file. + +```bash +./vendor/bin/sake dev/tasks/TagsToShortcodeTask +``` + ### Any other script that needs running. Some third party modules may include their own migration tasks. Take a minute to consult the release notes of your third party dependencies to make sure you haven't missed anything. diff --git a/src/Core/ClassInfo.php b/src/Core/ClassInfo.php index e51bbe81d..1ddbd2585 100644 --- a/src/Core/ClassInfo.php +++ b/src/Core/ClassInfo.php @@ -182,9 +182,11 @@ class ClassInfo * * * @param string|object $nameOrObject The classname or object + * @param bool $includeBaseClass Whether to include the base class or not. Defaults to true. * @return array List of class names with lowercase keys and correct-case values + * @throws \ReflectionException */ - public static function subclassesFor($nameOrObject) + public static function subclassesFor($nameOrObject, $includeBaseClass = true) { if (is_string($nameOrObject) && !class_exists($nameOrObject)) { return []; @@ -197,7 +199,7 @@ class ClassInfo // Merge with descendants $descendants = ClassLoader::inst()->getManifest()->getDescendantsOf($className); return array_merge( - [$lowerClassName => $className], + $includeBaseClass ? [$lowerClassName => $className] : [], $descendants ); } diff --git a/src/ORM/DataObjectSchema.php b/src/ORM/DataObjectSchema.php index ab694f28e..7ccf4d745 100644 --- a/src/ORM/DataObjectSchema.php +++ b/src/ORM/DataObjectSchema.php @@ -1242,4 +1242,54 @@ class DataObjectSchema ); } } + + /** + * Returns an array of the fields available for the provided class and its sub-classes as follows: + * + * [ + * 'ClassName' => [ + * 'TableName' => [ + * 'FieldName', + * 'FieldName2', + * ], + * 'TableName2' => [ + * 'FieldName3', + * ], + * ], + * ] + * + * + * @param string|object $baseClass + * @param bool $includeBaseClass Whether to include fields in the base class or not + * @param string|array $fieldNames The field to get mappings for, for example 'HTMLText'. Can also be an array. + * @return array An array of fields that derivec from $baseClass. + * @throws \ReflectionException + */ + public static function getFieldMap($baseClass, $includeBaseClass, $fieldNames) + { + $mapping = []; + + foreach (ClassInfo::subclassesFor($baseClass, $includeBaseClass) as $class) { + /** @var DataObjectSchema $schema */ + $schema = singleton($class)->getSchema(); + /** @var DataObject $fields */ + $fields = $schema->fieldSpecs($class); + foreach ($fields as $field => $type) { + if (in_array($type, $fieldNames)) { + $table = $schema->tableForField($class, $field); + if (!isset($mapping[$class])) { + $mapping[$class] = []; + } + if (!isset($mapping[$class][$table])) { + $mapping[$class][$table] = []; + } + if (!in_array($field, $mapping[$class][$table])) { + $mapping[$class][$table][] = $field; + } + } + } + } + + return $mapping; + } } diff --git a/tests/php/Core/ClassInfoTest.php b/tests/php/Core/ClassInfoTest.php index e45a062ea..1883451ad 100644 --- a/tests/php/Core/ClassInfoTest.php +++ b/tests/php/Core/ClassInfoTest.php @@ -54,6 +54,10 @@ class ClassInfoTest extends SapphireTest 'silverstripe\\core\\tests\\classinfotest\\childclass' => ChildClass::class, 'silverstripe\\core\\tests\\classinfotest\\grandchildclass' => GrandChildClass::class, ]; + $subclassesWithoutBase = [ + 'silverstripe\\core\\tests\\classinfotest\\childclass' => ChildClass::class, + 'silverstripe\\core\\tests\\classinfotest\\grandchildclass' => GrandChildClass::class, + ]; $this->assertEquals( $subclasses, ClassInfo::subclassesFor(BaseClass::class), @@ -65,6 +69,11 @@ class ClassInfoTest extends SapphireTest ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass'), 'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not' ); + ClassInfo::reset_db_cache(); + $this->assertEquals( + $subclassesWithoutBase, + ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass', false) + ); } public function testClassName() diff --git a/tests/php/ORM/DataObjectTest.php b/tests/php/ORM/DataObjectTest.php index 88b081c0d..e261faf12 100644 --- a/tests/php/ORM/DataObjectTest.php +++ b/tests/php/ORM/DataObjectTest.php @@ -4,6 +4,8 @@ namespace SilverStripe\ORM\Tests; use InvalidArgumentException; use LogicException; +use Page; +use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Core\Config\Config; use SilverStripe\Dev\SapphireTest; use SilverStripe\i18n\i18n; @@ -18,7 +20,9 @@ use SilverStripe\ORM\FieldType\DBPolymorphicForeignKey; use SilverStripe\ORM\FieldType\DBVarchar; use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\Tests\DataObjectTest\Company; +use SilverStripe\ORM\Tests\DataObjectTest\OtherSubclassWithSameField; use SilverStripe\ORM\Tests\DataObjectTest\Player; +use SilverStripe\ORM\Tests\DataObjectTest\SubTeam; use SilverStripe\ORM\Tests\DataObjectTest\Team; use SilverStripe\View\ViewableData; use stdClass; @@ -2404,4 +2408,20 @@ class DataObjectTest extends SapphireTest $do->write(); } + + public function testGetFieldMap() + { + $classes = DataObjectSchema::getFieldMap(DataObject::class, false, ['HTMLVarchar', 'Varchar']); + + $this->assertEquals('Title', $classes[Team::class]['DataObjectTest_Team'][1]); + $this->assertEquals( + 'SubclassDatabaseField', + $classes[SubTeam::class]['DataObjectTest_SubTeam'][0] + ); + + $classes = DataObjectSchema::getFieldMap(SubTeam::class, true, ['HTMLVarchar']); + + $this->assertFalse(isset($classes[Team::class])); + $this->assertEquals('DatabaseField', $classes[SubTeam::class]['DataObjectTest_Team'][0]); + } }