From 0075bf6e49973ac357a56a55d1053b86497fe98b Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Wed, 22 Feb 2023 09:40:27 +1300 Subject: [PATCH] NEW Access dynamic data inside ViewableData --- src/ORM/FieldType/DBComposite.php | 9 +++++---- src/View/ViewableData.php | 23 +++++++++++++++++++---- tests/php/ORM/DBCompositeTest.php | 12 ++++++++++++ tests/php/View/ViewableDataTest.php | 11 +++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/ORM/FieldType/DBComposite.php b/src/ORM/FieldType/DBComposite.php index 05423ce08..a4d936b2e 100644 --- a/src/ORM/FieldType/DBComposite.php +++ b/src/ORM/FieldType/DBComposite.php @@ -2,6 +2,7 @@ namespace SilverStripe\ORM\FieldType; +use InvalidArgumentException; use SilverStripe\Core\Injector\Injector; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; @@ -270,11 +271,11 @@ abstract class DBComposite extends DBField { $this->objCacheClear(); - // Non-db fields get assigned as normal properties if (!$this->hasField($field)) { - parent::setField($field, $value); - - return $this; + throw new InvalidArgumentException(implode(' ', [ + "Field $field does not exist.", + 'If this was accessed via a dynamic property then call setDynamicData() instead.' + ])); } // Set changed diff --git a/src/View/ViewableData.php b/src/View/ViewableData.php index 77d25ae4f..7c3d3f814 100644 --- a/src/View/ViewableData.php +++ b/src/View/ViewableData.php @@ -73,7 +73,7 @@ class ViewableData implements IteratorAggregate /** * Acts as a PHP 8.2+ compliant replacement for dynamic properties */ - private array $data = []; + private array $dynamicData = []; // ----------------------------------------------------------------------------------------------------------------- @@ -203,7 +203,7 @@ class ViewableData implements IteratorAggregate */ public function hasField($field) { - return property_exists($this, $field) || isset($this->data[$field]); + return property_exists($this, $field) || $this->hasDynamicData($field); } /** @@ -217,7 +217,7 @@ class ViewableData implements IteratorAggregate if ($this->isAccessibleProperty($field)) { return $this->$field; } - return $this->data[$field]; + return $this->getDynamicData($field); } /** @@ -236,10 +236,25 @@ class ViewableData implements IteratorAggregate if ($this->isAccessibleProperty($field)) { $this->$field = $value; } - $this->data[$field] = $value; + return $this->setDynamicData($field, $value); + } + + public function getDynamicData(string $field): mixed + { + return $this->hasDynamicData($field) ? $this->dynamicData[$field] : null; + } + + public function setDynamicData(string $field, mixed $value): static + { + $this->dynamicData[$field] = $value; return $this; } + public function hasDynamicData(string $field): bool + { + return array_key_exists($field, $this->dynamicData); + } + /** * Returns true if a method exists for the current class which isn't private. * Also returns true for private methods if $this is ViewableData (not a subclass) diff --git a/tests/php/ORM/DBCompositeTest.php b/tests/php/ORM/DBCompositeTest.php index cbad5910c..4afd93ff0 100644 --- a/tests/php/ORM/DBCompositeTest.php +++ b/tests/php/ORM/DBCompositeTest.php @@ -5,6 +5,7 @@ namespace SilverStripe\ORM\Tests; use SilverStripe\ORM\FieldType\DBMoney; use SilverStripe\ORM\DataObject; use SilverStripe\Dev\SapphireTest; +use InvalidArgumentException; class DBCompositeTest extends SapphireTest { @@ -108,4 +109,15 @@ class DBCompositeTest extends SapphireTest $this->assertEquals('DBCompositeTest_SubclassedDBFieldObject', $object2->dbObject('OtherMoney')->getTable()); $this->assertEquals('DBCompositeTest_SubclassedDBFieldObject', $object2->dbObject('OverriddenMoney')->getTable()); } + + public function testSetFieldDynamicPropertyException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage(implode(' ', [ + 'Field abc does not exist.', + 'If this was accessed via a dynamic property then call setDynamicData() instead.' + ])); + $object = new DBCompositeTest\TestObject(); + $object->MyMoney->abc = 'def'; + } } diff --git a/tests/php/View/ViewableDataTest.php b/tests/php/View/ViewableDataTest.php index 5ddfb0399..15ff74252 100644 --- a/tests/php/View/ViewableDataTest.php +++ b/tests/php/View/ViewableDataTest.php @@ -267,4 +267,15 @@ class ViewableDataTest extends SapphireTest $output = $reflectionMethod->invokeArgs(new ViewableData(), ['objCache']); $this->assertTrue($output, 'Property should be accessible'); } + + public function testDynamicData() + { + $obj = (object) ['SomeField' => [1, 2, 3]]; + $viewableData = new ViewableData(); + $this->assertFalse($viewableData->hasDynamicData('abc')); + $viewableData->setDynamicData('abc', $obj); + $this->assertTrue($viewableData->hasDynamicData('abc')); + $this->assertSame($obj, $viewableData->getDynamicData('abc')); + $this->assertSame($obj, $viewableData->abc); + } }