From c22a7f6917da5c5f56f275c3ca9c0c4a63205216 Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Wed, 21 Oct 2026 10:01:10 +0200 Subject: [PATCH] Ensure `fromDb()` is idempotent for all behaviors The `Hydrator` doesn't apply beahviors for columns with custom aliases, so to allow consumers to apply behaviors manually while remaining forward compatible once the `Hydrator` is fixed all behaviors should have a idempotent `fromDb()`. This is already the case for `Binary`, `BoolCast` and `EnumCast`. `MillisecondTimestamp` and `UUID` are adjusted as they would previously throw when applied twice. --- src/Behavior/MillisecondTimestamp.php | 4 ++-- src/Behavior/UUID.php | 4 ++-- tests/Behavior/UUIDTest.php | 16 ++++++++++++++++ tests/MillisecondTimestampBehaviorTest.php | 8 ++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Behavior/MillisecondTimestamp.php b/src/Behavior/MillisecondTimestamp.php index 76b818b4..f23a89c0 100644 --- a/src/Behavior/MillisecondTimestamp.php +++ b/src/Behavior/MillisecondTimestamp.php @@ -12,8 +12,8 @@ class MillisecondTimestamp extends PropertyBehavior { public function fromDb($value, $key, $context) { - if ($value === null) { - return null; + if ($value === null || $value instanceof DateTime) { + return $value; } $datetime = DateTime::createFromFormat('U.u', sprintf('%F', $value / 1000.0)); diff --git a/src/Behavior/UUID.php b/src/Behavior/UUID.php index 85072e1b..3bc93c9d 100644 --- a/src/Behavior/UUID.php +++ b/src/Behavior/UUID.php @@ -20,8 +20,8 @@ class UUID extends PropertyBehavior implements QueryAwareBehavior public function fromDb($value, $key, $_) { - if ($value === null) { - return null; + if ($value === null || $value instanceof UuidInterface) { + return $value; } if (! is_string($value)) { diff --git a/tests/Behavior/UUIDTest.php b/tests/Behavior/UUIDTest.php index 39af4363..a6144993 100644 --- a/tests/Behavior/UUIDTest.php +++ b/tests/Behavior/UUIDTest.php @@ -38,6 +38,22 @@ public function testRetrievePropertyReturnsUuidInstance(): void ); } + public function testRetrievePropertyCanBeAppliedRepeatedly(): void + { + $behavior = $this->behavior(); + $uuid = $behavior->retrieveProperty( + RamseyUuid::fromString(static::TEST_UUID_VALUE)->getBytes(), + static::TEST_COLUMN + ); + + $this->assertSame($uuid, $behavior->retrieveProperty($uuid, static::TEST_COLUMN)); + + $behavior = $this->behavior(true); + $uuid = $behavior->retrieveProperty(static::TEST_UUID_VALUE, static::TEST_COLUMN); + + $this->assertSame($uuid, $behavior->retrieveProperty($uuid, static::TEST_COLUMN)); + } + public function testPersistPropertyReturnsNullWhenValueIsNull(): void { $this->assertNull($this->behavior()->persistProperty(null, static::TEST_COLUMN)); diff --git a/tests/MillisecondTimestampBehaviorTest.php b/tests/MillisecondTimestampBehaviorTest.php index a1045e42..07d71ddb 100644 --- a/tests/MillisecondTimestampBehaviorTest.php +++ b/tests/MillisecondTimestampBehaviorTest.php @@ -15,6 +15,14 @@ public function testFromDbReturnsNullWhenNullIsPassed() $this->assertNull((new MillisecondTimestamp([]))->fromDb(null, 'key', null)); } + public function testFromDbCanBeAppliedRepeatedly() + { + $behavior = new MillisecondTimestamp([]); + $sometime = $behavior->fromDb(1668002400123, 'key', null); + + $this->assertSame($sometime, $behavior->fromDb($sometime, 'key', null)); + } + public function testToDbReturnsNullWhenNullIsPassed() { $this->assertNull((new MillisecondTimestamp([]))->toDb(null, 'key', null));