diff --git a/src/Core/Injector/Injector.php b/src/Core/Injector/Injector.php index 6b0885132..6cf980cbf 100644 --- a/src/Core/Injector/Injector.php +++ b/src/Core/Injector/Injector.php @@ -13,6 +13,7 @@ use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Environment; use SilverStripe\Dev\Deprecation; +use SilverStripe\ORM\DataObject; /** * A simple injection manager that manages creating objects and injecting @@ -581,6 +582,14 @@ class Injector implements ContainerInterface $constructorParams = $spec['constructor']; } + // If we're dealing with a DataObject singleton without specific constructor params, pass through Singleton + // flag as second argument + if ((!$type || $type !== self::PROTOTYPE) + && empty($constructorParams) + && is_subclass_of($class, DataObject::class)) { + $constructorParams = array(null, true); + } + $factory = isset($spec['factory']) ? $this->get($spec['factory']) : $this->getObjectCreator(); $object = $factory->create($class, $constructorParams); diff --git a/tests/php/ORM/DataObjectTest.php b/tests/php/ORM/DataObjectTest.php index 55c2ea659..3a2158c4c 100644 --- a/tests/php/ORM/DataObjectTest.php +++ b/tests/php/ORM/DataObjectTest.php @@ -66,6 +66,51 @@ class DataObjectTest extends SapphireTest ); } + /** + * @dataProvider provideSingletons + */ + public function testSingleton($inst, $defaultValue, $altDefaultValue) + { + $inst = $inst(); + // Test that populateDefaults() isn't called on singletons + // which can lead to SQL errors during build, and endless loops + if ($defaultValue) { + $this->assertEquals($defaultValue, $inst->MyFieldWithDefault); + } else { + $this->assertEmpty($inst->MyFieldWithDefault); + } + + if ($altDefaultValue) { + $this->assertEquals($altDefaultValue, $inst->MyFieldWithAltDefault); + } else { + $this->assertEmpty($inst->MyFieldWithAltDefault); + } + } + + public function provideSingletons() + { + // because PHPUnit evalutes test providers *before* setUp methods + // any extensions added in the setUp methods won't be available + // we must return closures to generate the arguments at run time + return array( + 'create() static method' => array(function () { + return DataObjectTest\Fixture::create(); + }, 'Default Value', 'Default Value'), + 'New object creation' => array(function () { + return new DataObjectTest\Fixture(); + }, 'Default Value', 'Default Value'), + 'singleton() function' => array(function () { + return singleton(DataObjectTest\Fixture::class); + }, null, null), + 'singleton() static method' => array(function () { + return DataObjectTest\Fixture::singleton(); + }, null, null), + 'Manual constructor args' => array(function () { + return new DataObjectTest\Fixture(null, true); + }, null, null), + ); + } + public function testDb() { $schema = DataObject::getSchema();