diff --git a/src/ORM/Connect/TableBuilder.php b/src/ORM/Connect/TableBuilder.php
new file mode 100644
index 00000000000..7b2e3cc41ef
--- /dev/null
+++ b/src/ORM/Connect/TableBuilder.php
@@ -0,0 +1,69 @@
+schemaUpdate(function () use ($dataClasses, $extraDataObjects, $testMode, $quiet, $showRecordCounts) {
+ $dataObjectSchema = DataObject::getSchema();
+
+ foreach ($dataClasses as $dataClass) {
+ // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
+ if (!class_exists($dataClass)) {
+ continue;
+ }
+
+ // Check if this class should be excluded as per testing conventions
+ /** @var DataObject $SNG */
+ $SNG = new $dataClass([], DataObject::CREATE_SINGLETON);
+ if (!$testMode && $SNG instanceof TestOnly) {
+ continue;
+ }
+
+ // Log data
+ if (!$quiet) {
+ $tableName = $dataObjectSchema->tableName($dataClass);
+ if ($showRecordCounts && DB::get_schema()->hasTable($tableName)) {
+ try {
+ $count = DB::query("SELECT COUNT(*) FROM \"$tableName\"")->value();
+ $countSuffix = " ($count records)";
+ } catch (\Exception $e) {
+ $countSuffix = " (error getting record count)";
+ }
+ } else {
+ $countSuffix = "";
+ }
+
+ if (Director::is_cli()) {
+ echo " * $tableName$countSuffix\n";
+ } else {
+ echo "
$tableName$countSuffix\n";
+ }
+ }
+
+ // Instruct the class to apply its schema to the database
+ $SNG->requireTable();
+ }
+
+ // If we have additional dataobjects which need schema (i.e. for tests), do so here:
+ if ($extraDataObjects) {
+ foreach ($extraDataObjects as $dataClass) {
+ $SNG = new $dataClass([], DataObject::CREATE_SINGLETON);
+ if ($SNG instanceof DataObject) {
+ $SNG->requireTable();
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/src/ORM/Connect/TempDatabase.php b/src/ORM/Connect/TempDatabase.php
index a268adf8943..ced43d8469d 100644
--- a/src/ORM/Connect/TempDatabase.php
+++ b/src/ORM/Connect/TempDatabase.php
@@ -244,29 +244,9 @@ protected function rebuildTables($extraDataObjects = [])
$schema = $this->getConn()->getSchemaManager();
$schema->quiet();
- $schema->schemaUpdate(
- function () use ($dataClasses, $extraDataObjects) {
- foreach ($dataClasses as $dataClass) {
- // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
- if (class_exists($dataClass ?? '')) {
- $SNG = singleton($dataClass);
- if (!($SNG instanceof TestOnly)) {
- $SNG->requireTable();
- }
- }
- }
- // If we have additional dataobjects which need schema, do so here:
- if ($extraDataObjects) {
- foreach ($extraDataObjects as $dataClass) {
- $SNG = singleton($dataClass);
- if (singleton($dataClass) instanceof DataObject) {
- $SNG->requireTable();
- }
- }
- }
- }
- );
+ $tableBuilder = TableBuilder::singleton();
+ $tableBuilder->buildTables($schema, $dataClasses, $extraDataObjects, true);
Config::modify()->set(DBSchemaManager::class, 'check_and_repair_on_build', $oldCheckAndRepairOnBuild);
diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php
index b795690ee26..c10ef679b45 100644
--- a/src/ORM/DatabaseAdmin.php
+++ b/src/ORM/DatabaseAdmin.php
@@ -14,6 +14,7 @@
use SilverStripe\Dev\DevelopmentAdmin;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\Connect\DatabaseException;
+use SilverStripe\ORM\Connect\TableBuilder;
use SilverStripe\ORM\FieldType\DBClassName;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
@@ -304,46 +305,8 @@ public function doBuild($quiet = false, $populate = true, $testMode = false)
// Initiate schema update
$dbSchema = DB::get_schema();
- $dbSchema->schemaUpdate(function () use ($dataClasses, $testMode, $quiet, $showRecordCounts) {
- $dataObjectSchema = DataObject::getSchema();
-
- foreach ($dataClasses as $dataClass) {
- // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
- if (!class_exists($dataClass ?? '')) {
- continue;
- }
-
- // Check if this class should be excluded as per testing conventions
- $SNG = singleton($dataClass);
- if (!$testMode && $SNG instanceof TestOnly) {
- continue;
- }
- $tableName = $dataObjectSchema->tableName($dataClass);
-
- // Log data
- if (!$quiet) {
- if ($showRecordCounts && DB::get_schema()->hasTable($tableName)) {
- try {
- $count = DB::query("SELECT COUNT(*) FROM \"$tableName\"")->value();
- $countSuffix = " ($count records)";
- } catch (Exception $e) {
- $countSuffix = " (error getting record count)";
- }
- } else {
- $countSuffix = "";
- }
-
- if (Director::is_cli()) {
- echo " * $tableName$countSuffix\n";
- } else {
- echo "$tableName$countSuffix\n";
- }
- }
-
- // Instruct the class to apply its schema to the database
- $SNG->requireTable();
- }
- });
+ $tableBuilder = TableBuilder::singleton();
+ $tableBuilder->buildTables($dbSchema, $dataClasses, [], $quiet, $testMode, $showRecordCounts);
ClassInfo::reset_db_cache();
if (!$quiet && !Director::is_cli()) {
diff --git a/tests/php/ORM/DataObjectTest.php b/tests/php/ORM/DataObjectTest.php
index 6bc8350ded0..9ec9e162990 100644
--- a/tests/php/ORM/DataObjectTest.php
+++ b/tests/php/ORM/DataObjectTest.php
@@ -5,6 +5,7 @@
use InvalidArgumentException;
use LogicException;
use SilverStripe\Core\Config\Config;
+use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\i18n\i18n;
use SilverStripe\ORM\Connect\MySQLDatabase;
@@ -63,11 +64,14 @@ class DataObjectTest extends SapphireTest
DataObjectTest\RelationChildSecond::class,
DataObjectTest\MockDynamicAssignmentDataObject::class,
DataObjectTest\TreeNode::class,
+ DataObjectTest\OverriddenDataObject::class,
+ DataObjectTest\InjectedDataObject::class,
];
protected function setUp(): void
{
parent::setUp();
+ Config::modify()->merge(Injector::class, DataObjectTest\OverriddenDataObject::class, ['class' => DataObjectTest\InjectedDataObject::class]);
$validator = Member::password_validator();
if ($validator) {
@@ -186,6 +190,26 @@ public function testDb()
);
}
+ public function testTableBuiltForInjectedDataObject()
+ {
+ // Test we get the correct injected class
+ $obj = DataObjectTest\OverriddenDataObject::create();
+ $this->assertSame(DataObjectTest\InjectedDataObject::class, get_class($obj));
+
+ // Test both tables are built
+ $schema = DataObject::getSchema();
+ $this->assertTrue($schema->classHasTable(DataObjectTest\OverriddenDataObject::class));
+ $this->assertTrue($schema->classHasTable(DataObjectTest\InjectedDataObject::class));
+
+ // Test fields from both the overridden and injected class exist
+ $obj->EmploymentType = 'Some type';
+ $obj->NewField = 'Some value';
+ $obj->write();
+ $objFromOrm = DataObjectTest\OverriddenDataObject::get()->first();
+ $this->assertSame('Some type', $objFromOrm->EmploymentType);
+ $this->assertSame('Some value', $objFromOrm->NewField);
+ }
+
public function testConstructAcceptsValues()
{
// Values can be an array...
diff --git a/tests/php/ORM/DataObjectTest/InjectedDataObject.php b/tests/php/ORM/DataObjectTest/InjectedDataObject.php
new file mode 100644
index 00000000000..0d6a117aed4
--- /dev/null
+++ b/tests/php/ORM/DataObjectTest/InjectedDataObject.php
@@ -0,0 +1,12 @@
+ 'Varchar',
+ ];
+}
diff --git a/tests/php/ORM/DataObjectTest/OverriddenDataObject.php b/tests/php/ORM/DataObjectTest/OverriddenDataObject.php
new file mode 100644
index 00000000000..97242185394
--- /dev/null
+++ b/tests/php/ORM/DataObjectTest/OverriddenDataObject.php
@@ -0,0 +1,20 @@
+ 'BigInt',
+ 'EmploymentType' => 'Varchar',
+ ];
+
+ private static $has_one = [
+ 'CurrentCompany' => Company::class,
+ ];
+}