diff --git a/src/ORM/DataObject.php b/src/ORM/DataObject.php index 0bee83564c5..74a3efc69ba 100644 --- a/src/ORM/DataObject.php +++ b/src/ORM/DataObject.php @@ -462,6 +462,7 @@ private function hydrate(array $record, bool $mustHaveID) static::class, DataObjectSchema::INCLUDE_CLASS | DataObjectSchema::DB_ONLY ); + foreach ($fields as $field => $fieldSpec) { $fieldClass = strtok($fieldSpec, "."); if (!array_key_exists($field, $record)) { @@ -469,6 +470,21 @@ private function hydrate(array $record, bool $mustHaveID) } } + // Extension point to hydrate additional fields into this object during construction. + // Return an array of field names => raw values from your augmentHydrateFields extension method. + $extendedAdditionalFields = $this->extend('augmentHydrateFields'); + foreach ($extendedAdditionalFields as $additionalFields) { + foreach ($additionalFields as $field => $value) { + $this->record[$field] = $value; + + // If a corresponding lazy-load field exists, remove it as the value has been provided + $lazyName = $field . '_Lazy'; + if (array_key_exists($lazyName, $this->record)) { + unset($this->record[$lazyName]); + } + } + } + $this->original = $this->record; $this->changed = []; $this->changeForced = false; diff --git a/tests/php/ORM/DataObjectTest.php b/tests/php/ORM/DataObjectTest.php index 8ad2cc166e5..3c243b84596 100644 --- a/tests/php/ORM/DataObjectTest.php +++ b/tests/php/ORM/DataObjectTest.php @@ -19,6 +19,7 @@ use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\Tests\DataObjectTest\Company; use SilverStripe\ORM\Tests\DataObjectTest\Player; +use SilverStripe\ORM\Tests\DataObjectTest\Team; use SilverStripe\ORM\Tests\DataObjectTest\TreeNode; use SilverStripe\Security\Member; use SilverStripe\View\ViewableData; @@ -211,6 +212,31 @@ public function testConstructAcceptsValues() $this->assertSame(5, $player->ID); } + /** + * @see SilverStripe\ORM\Tests\DataObjectTest\Team_Extension + */ + public function testConstructHydratesAugmentedValues() + { + // When creating a DataObject from singleton, DataObject::hydrate() isn't called + $team = new Team([], DataObject::CREATE_SINGLETON); + $this->assertNull($team->CustomHydratedField); + + // Similarly, when hydrating by creating a DataObject from nothing, hydrate() isn't called + $team2 = new Team([]); + $id = $team2->write(); + $this->assertNull($team2->CustomHydratedField); + + // However when rebuilding an object from the database, it is and we can expect our extension to execute + /** @var Team $team3 */ + $team3 = Team::get()->byID($id); + $this->assertTrue($team3->CustomHydratedField); + + // Also when rebuilding an object in memory, hydrate() is called and our extension should execute + /** @var Team $team4 */ + $team4 = $team->newClassInstance(Team::class); + $this->assertTrue($team4->CustomHydratedField); + } + public function testValidObjectsForBaseFields() { $obj = new DataObjectTest\ValidatedObject(); diff --git a/tests/php/ORM/DataObjectTest/Team.php b/tests/php/ORM/DataObjectTest/Team.php index d559a1ef120..211b8dc2a95 100644 --- a/tests/php/ORM/DataObjectTest/Team.php +++ b/tests/php/ORM/DataObjectTest/Team.php @@ -12,6 +12,7 @@ * @property string DatabaseField * @property array SalaryCap * @property string FoundationYear + * @property bool CustomHydratedField * @method Player Captain() * @method Player Founder() * @method Player HasOneRelationship() diff --git a/tests/php/ORM/DataObjectTest/Team_Extension.php b/tests/php/ORM/DataObjectTest/Team_Extension.php index 14d18ac1568..1129b61f023 100644 --- a/tests/php/ORM/DataObjectTest/Team_Extension.php +++ b/tests/php/ORM/DataObjectTest/Team_Extension.php @@ -23,4 +23,11 @@ public function getExtendedDynamicField() { return "extended dynamic field"; } + + public function augmentHydrateFields() + { + return [ + 'CustomHydratedField' => true, + ]; + } }