diff --git a/src/ORM/FieldType/DBDatetime.php b/src/ORM/FieldType/DBDatetime.php index f4859635f..96cdc25ed 100644 --- a/src/ORM/FieldType/DBDatetime.php +++ b/src/ORM/FieldType/DBDatetime.php @@ -47,6 +47,46 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider */ const ISO_DATETIME_NORMALISED = 'y-MM-dd\'T\'HH:mm:ss'; + /** + * Flag idicating if this field is considered immutable + * when this is enabled setting the value of this field will return a new field instance + * instead updatin the old one + * + * @var bool + */ + protected $immutable = false; + + /** + * @param bool $immutable + * @return $this + */ + public function setImmutable(bool $immutable): self + { + $this->immutable = $immutable; + + return $this; + } + + public function setValue($value, $record = null, $markChanged = true) + { + if ($this->immutable) { + // This field is set as immutable so we have to create a new field instance + // instead of just updating the value + $field = clone $this; + + return $field + // This field will inherit the immutable status but we have to disable it before setting the value + // to avoid recursion + ->setImmutable(false) + // Update the value so the new field contains the desired value + ->setValue($value, $record, $markChanged) + // Return the immutable flag to the correct state + ->setImmutable(true); + } + + return parent::setValue($value, $record, $markChanged); + } + /** * Returns the standard localised date * @@ -170,13 +210,11 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider */ public static function now() { + $time = self::$mock_now ? self::$mock_now->Value : time(); + /** @var DBDatetime $now */ - $now = null; - if (self::$mock_now) { - $now = DBField::create_field('Datetime', self::$mock_now->Value); - } else { - $now = DBField::create_field('Datetime', time()); - } + $now = DBField::create_field('Datetime', $time); + return $now; } diff --git a/tests/php/Forms/DatetimeFieldTest.php b/tests/php/Forms/DatetimeFieldTest.php index 6fa5e39a7..cf77ab75f 100644 --- a/tests/php/Forms/DatetimeFieldTest.php +++ b/tests/php/Forms/DatetimeFieldTest.php @@ -499,6 +499,24 @@ class DatetimeFieldTest extends SapphireTest $field->setTimezone('Pacific/Auckland'); } + public function testModifyReturnNewField(): void + { + $globalStateNow = '2020-01-01 00:00:00'; + DBDatetime::set_mock_now($globalStateNow); + + // Suppose we need to know the current time in our feature, we store it in a variable + // Make this field immutable, so future modifications don't apply to any other object references + $now = DBDatetime::now()->setImmutable(true); + + // Later in the code we want to know the time value for 10 days later, we can reuse our $now variable + $later = $now->modify('+ 10 days')->Rfc2822(); + + // Our expectation is that this code should not apply the change to our + // $now variable declared earlier in the code + $this->assertSame('2020-01-11 00:00:00', $later, 'We expect to get a future datetime'); + $this->assertSame($globalStateNow, $now->Rfc2822(), 'We expect to get the current datetime'); + } + protected function getMockForm() { /** @skipUpgrade */