Add `getFieldMap` method to retrieve a list of all fields for any giv… (#8892)

* Add `getFieldMap` method to retrieve a list of all fields for any given class

* Add `TagsToShortcodeTask` to upgrading guide

Adding after the file migration part as this is where it makes the most sense to run it.

* `getFieldMap` accepts an array

* Move to `DataObjectSchema`

* Add `HTMLVarchar` to documentation
Minor refactoring

* Add test for checking that `subclassesfor` works without the base class
Add test `DataObjectSchema::getFieldMap` returns the correct array

* Remove cms dependency
This commit is contained in:
Andre Kiste 2019-04-30 10:43:14 +12:00 committed by Aaron Carlino
parent e648fd31f9
commit 0c6c57f1ef
5 changed files with 91 additions and 2 deletions

View File

@ -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.

View File

@ -182,9 +182,11 @@ class ClassInfo
* </code>
*
* @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
);
}

View File

@ -1242,4 +1242,54 @@ class DataObjectSchema
);
}
}
/**
* Returns an array of the fields available for the provided class and its sub-classes as follows:
* <code>
* [
* 'ClassName' => [
* 'TableName' => [
* 'FieldName',
* 'FieldName2',
* ],
* 'TableName2' => [
* 'FieldName3',
* ],
* ],
* ]
* </code>
*
* @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;
}
}

View File

@ -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()

View File

@ -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]);
}
}