diff --git a/src/ORM/FieldType/DBBoolean.php b/src/ORM/FieldType/DBBoolean.php index a0b485f5d..46374682c 100644 --- a/src/ORM/FieldType/DBBoolean.php +++ b/src/ORM/FieldType/DBBoolean.php @@ -46,7 +46,11 @@ class DBBoolean extends DBField { $fieldName = $this->name; if ($fieldName) { - $dataObject->setField($fieldName, $this->value ? 1 : 0); + if ($this->value instanceof DBField) { + $this->value->saveInto($dataObject); + } else { + $dataObject->__set($fieldName, $this->value ? 1 : 0); + } } else { $class = static::class; throw new \RuntimeException("DBField::saveInto() Called on a nameless '$class' object"); diff --git a/src/ORM/FieldType/DBComposite.php b/src/ORM/FieldType/DBComposite.php index a4d936b2e..2447f3df9 100644 --- a/src/ORM/FieldType/DBComposite.php +++ b/src/ORM/FieldType/DBComposite.php @@ -221,8 +221,12 @@ abstract class DBComposite extends DBField { foreach ($this->compositeDatabaseFields() as $field => $spec) { // Save into record - $key = $this->getName() . $field; - $dataObject->setField($key, $this->getField($field)); + if ($this->value instanceof DBField) { + $this->value->saveInto($dataObject); + } else { + $key = $this->getName() . $field; + $dataObject->__set($key, $this->getField($field)); + } } } diff --git a/src/ORM/FieldType/DBDecimal.php b/src/ORM/FieldType/DBDecimal.php index a8c9df99b..dcf2d7e80 100644 --- a/src/ORM/FieldType/DBDecimal.php +++ b/src/ORM/FieldType/DBDecimal.php @@ -88,8 +88,12 @@ class DBDecimal extends DBField $fieldName = $this->name; if ($fieldName) { - $value = (float) preg_replace('/[^0-9.\-\+]/', '', $this->value ?? ''); - $dataObject->setField($fieldName, $value); + if ($this->value instanceof DBField) { + $this->value->saveInto($dataObject); + } else { + $value = (float) preg_replace('/[^0-9.\-\+]/', '', $this->value ?? ''); + $dataObject->__set($fieldName, $value); + } } else { throw new \UnexpectedValueException( "DBField::saveInto() Called on a nameless '" . static::class . "' object" diff --git a/src/ORM/FieldType/DBField.php b/src/ORM/FieldType/DBField.php index 0c96442fc..520ce73d8 100644 --- a/src/ORM/FieldType/DBField.php +++ b/src/ORM/FieldType/DBField.php @@ -542,7 +542,11 @@ abstract class DBField extends ViewableData implements DBIndexable "DBField::saveInto() Called on a nameless '" . static::class . "' object" ); } - $dataObject->setField($fieldName, $this->value); + if ($this->value instanceof self) { + $this->value->saveInto($dataObject); + } else { + $dataObject->__set($fieldName, $this->value); + } } /** diff --git a/src/ORM/FieldType/DBPercentage.php b/src/ORM/FieldType/DBPercentage.php index dca55431c..1abf613a2 100644 --- a/src/ORM/FieldType/DBPercentage.php +++ b/src/ORM/FieldType/DBPercentage.php @@ -45,7 +45,7 @@ class DBPercentage extends DBDecimal $fieldName = $this->name; if ($fieldName && $dataObject->$fieldName > 1.0) { - $dataObject->setField($fieldName, 1.0); + $dataObject->__set($fieldName, 1.0); } } } diff --git a/tests/php/ORM/DBFieldTest.php b/tests/php/ORM/DBFieldTest.php index 4c106b7d9..8168137b5 100644 --- a/tests/php/ORM/DBFieldTest.php +++ b/tests/php/ORM/DBFieldTest.php @@ -27,6 +27,7 @@ use SilverStripe\ORM\FieldType\DBTime; use SilverStripe\ORM\FieldType\DBVarchar; use SilverStripe\ORM\FieldType\DBText; use SilverStripe\Dev\SapphireTest; +use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBYear; /** @@ -34,6 +35,9 @@ use SilverStripe\ORM\FieldType\DBYear; */ class DBFieldTest extends SapphireTest { + protected static $extra_dataobjects = [ + DBFieldTest\TestDataObject::class, + ]; /** * Test the nullValue() method on DBField. @@ -322,4 +326,73 @@ class DBFieldTest extends SapphireTest $this->assertEquals('
ÅÄÖ
', DBHTMLText::create_field('HTMLFragment', 'åäö
')->UpperCase()); $this->assertEquals('åäö
', DBHTMLText::create_field('HTMLFragment', 'ÅÄÖ
')->LowerCase()); } + + public function testSaveInto() + { + $obj = new DBFieldTest\TestDataObject(); + /** @var DBField $field */ + $field = $obj->dbObject('Title'); + $field->setValue('New Value'); + $field->saveInto($obj); + + $this->assertEquals('New Value', $obj->getField('Title')); + $this->assertEquals(1, $field->saveIntoCalledCount); + $this->assertEquals(1, $obj->setFieldCalledCount); + } + + public function testSaveIntoNoRecursion() + { + $obj = new DBFieldTest\TestDataObject(); + /** @var DBField $field */ + $field = $obj->dbObject('Title'); + $value = new DBFieldTest\TestDbField('Title'); + $value->setValue('New Value'); + $field->setValue($value); + $field->saveInto($obj); + + $this->assertEquals('New Value', $obj->getField('Title')); + $this->assertEquals(1, $field->saveIntoCalledCount); + $this->assertEquals(1, $obj->setFieldCalledCount); + } + + public function testSaveIntoAsProperty() + { + $obj = new DBFieldTest\TestDataObject(); + /** @var DBField $field */ + $field = $obj->dbObject('Title'); + $field->setValue('New Value'); + $obj->Title = $field; + + $this->assertEquals('New Value', $obj->getField('Title')); + $this->assertEquals(1, $field->saveIntoCalledCount); + // Called twice because $obj->setField($field) => $field->saveInto() => $obj->setField('New Value') + $this->assertEquals(2, $obj->setFieldCalledCount); + } + + public function testSaveIntoNoRecursionAsProperty() + { + $obj = new DBFieldTest\TestDataObject(); + /** @var DBField $field */ + $field = $obj->dbObject('Title'); + $value = new DBFieldTest\TestDbField('Title'); + $value->setValue('New Value'); + $field->setValue($value); + $obj->Title = $field; + + $this->assertEquals('New Value', $obj->getField('Title')); + $this->assertEquals(1, $field->saveIntoCalledCount); + // Called twice because $obj->setField($field) => $field->saveInto() => $obj->setField('New Value') + $this->assertEquals(2, $obj->setFieldCalledCount); + } + + public function testSaveIntoRespectsSetters() + { + $obj = new DBFieldTest\TestDataObject(); + /** @var DBField $field */ + $field = $obj->dbObject('MyTestField'); + $field->setValue('New Value'); + $obj->MyTestField = $field; + + $this->assertEquals('new value', $obj->getField('MyTestField')); + } } diff --git a/tests/php/ORM/DBFieldTest/TestDataObject.php b/tests/php/ORM/DBFieldTest/TestDataObject.php new file mode 100644 index 000000000..4d2efd374 --- /dev/null +++ b/tests/php/ORM/DBFieldTest/TestDataObject.php @@ -0,0 +1,29 @@ + TestDbField::class, + 'MyTestField' => TestDbField::class, + ]; + + public $setFieldCalledCount = 0; + + public function setField($fieldName, $val) + { + $this->setFieldCalledCount++; + return parent::setField($fieldName, $val); + } + + public function setMyTestField($val) + { + return $this->setField('MyTestField', strtolower($val)); + } +} diff --git a/tests/php/ORM/DBFieldTest/TestDbField.php b/tests/php/ORM/DBFieldTest/TestDbField.php new file mode 100644 index 000000000..deb9773c4 --- /dev/null +++ b/tests/php/ORM/DBFieldTest/TestDbField.php @@ -0,0 +1,42 @@ +get(MySQLDatabase::class, 'charset'); + $collation = Config::inst()->get(MySQLDatabase::class, 'collation'); + + $parts = [ + 'datatype' => 'varchar', + 'precision' => 255, + 'character set' => $charset, + 'collate' => $collation, + 'arrayValue' => $this->arrayValue + ]; + + $values = [ + 'type' => 'varchar', + 'parts' => $parts + ]; + + DB::require_field($this->tableName, $this->name, $values); + } + + public $saveIntoCalledCount = 0; + + public function saveInto($dataObject) + { + $this->saveIntoCalledCount++; + return parent::saveInto($dataObject); + } +} diff --git a/tests/php/ORM/DataObjectTest.php b/tests/php/ORM/DataObjectTest.php index 493840301..4139291ca 100644 --- a/tests/php/ORM/DataObjectTest.php +++ b/tests/php/ORM/DataObjectTest.php @@ -66,6 +66,7 @@ class DataObjectTest extends SapphireTest DataObjectTest\TreeNode::class, DataObjectTest\OverriddenDataObject::class, DataObjectTest\InjectedDataObject::class, + DataObjectTest\SettersAndGetters::class, ]; protected function setUp(): void @@ -2667,4 +2668,14 @@ class DataObjectTest extends SapphireTest $vals = ['25.25', '50.00', '75.00', '100.50']; $this->assertSame(array_combine($vals ?? [], $vals ?? []), $obj->dbObject('MyEnumWithDots')->enumValues()); } + + public function testSettersAndGettersAreRespected() + { + $obj = new DataObjectTest\SettersAndGetters(); + $obj->MyTestField = 'Some Value'; + // Setter overrides it with all lower case + $this->assertSame('some value', $obj->getField('MyTestField')); + // Getter overrides it with all upper case + $this->assertSame('SOME VALUE', $obj->MyTestField); + } } diff --git a/tests/php/ORM/DataObjectTest/SettersAndGetters.php b/tests/php/ORM/DataObjectTest/SettersAndGetters.php new file mode 100644 index 000000000..7d6ff9059 --- /dev/null +++ b/tests/php/ORM/DataObjectTest/SettersAndGetters.php @@ -0,0 +1,25 @@ + 'Varchar(255)', + ]; + + public function setMyTestField($val) + { + $this->setField('MyTestField', strtolower($val)); + } + + public function getMyTestField() + { + return strtoupper($this->getField('MyTestField')); + } +}