From 67a42dc7ca56c88b2bc8510bce21220c7699bb83 Mon Sep 17 00:00:00 2001 From: jalexiscv Date: Sun, 17 May 2026 21:57:35 -0500 Subject: [PATCH] fix: toRawArray() converts DateTime objects to strings Fixes #8302 When a date field is set on an Entity, __set() converts it to a Time object via mutateDate(). However, toRawArray() returned attributes directly without converting Time/DateTime objects back to strings. This fixes toRawArray() to return primitive types by converting DateTimeInterface objects to strings via __toString(). The underlying $this->attributes remain unchanged (still contain Time objects). toArray() behavior is preserved (still returns Time objects). Changes: - Entity::toRawArray(): DateTimeInterface objects converted to strings - Added test verifying: * toRawArray() returns strings for date fields * $this->attributes still contain Time objects * toArray() still returns Time objects (no regression) Ref: https://github.com/codeigniter4/CodeIgniter4/issues/8302 --- system/Entity/Entity.php | 11 ++++++---- tests/system/Entity/EntityTest.php | 35 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php index e3733bd0fc02..0d8bccbc3cd1 100644 --- a/system/Entity/Entity.php +++ b/system/Entity/Entity.php @@ -233,6 +233,11 @@ public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recu public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array { $convert = static function ($value) use (&$convert, $recursive) { + // Always convert DateTime objects to string for raw output + if ($value instanceof DateTimeInterface) { + return (string) $value; + } + if (! $recursive) { return $value; } @@ -261,9 +266,7 @@ public function toRawArray(bool $onlyChanged = false, bool $recursive = false): // When returning everything if (! $onlyChanged) { - return $recursive - ? array_map($convert, $this->attributes) - : $this->attributes; + return array_map($convert, $this->attributes); } // When filtering by changed values only @@ -335,7 +338,7 @@ public function toRawArray(bool $onlyChanged = false, bool $recursive = false): } // non-recursive changed value - $return[$key] = $value; + $return[$key] = $convert($value); } return $return; diff --git a/tests/system/Entity/EntityTest.php b/tests/system/Entity/EntityTest.php index 52d1de2f5a8b..c13bf1453aa9 100644 --- a/tests/system/Entity/EntityTest.php +++ b/tests/system/Entity/EntityTest.php @@ -367,6 +367,41 @@ public function testDateMutationTimeToTime(): void $this->assertCloseEnoughString($dt->format('Y-m-d H:i:s'), $time->format('Y-m-d H:i:s')); } + public function testToRawArrayConvertsDateTimeToString(): void + { + $entity = new class () extends Entity { + protected $attributes = [ + 'created_at' => null, + 'updated_at' => null, + ]; + protected $original = [ + 'created_at' => null, + 'updated_at' => null, + ]; + }; + + $entity->created_at = '2023-12-12 12:12:12'; + $entity->updated_at = '2023-12-13 13:13:13'; + + $raw = $entity->toRawArray(); + + // toRawArray() should return primitive types, not objects + $this->assertIsString($raw['created_at']); + $this->assertSame('2023-12-12 12:12:12', $raw['created_at']); + $this->assertIsString($raw['updated_at']); + $this->assertSame('2023-12-13 13:13:13', $raw['updated_at']); + + // Attributes themselves should still contain Time objects + $attrs = $this->getPrivateProperty($entity, 'attributes'); + $this->assertInstanceOf(Time::class, $attrs['created_at']); + $this->assertInstanceOf(Time::class, $attrs['updated_at']); + + // toArray() should still return Time objects (no regression) + $array = $entity->toArray(); + $this->assertInstanceOf(Time::class, $array['created_at']); + $this->assertInstanceOf(Time::class, $array['updated_at']); + } + public function testCastInteger(): void { $entity = $this->getCastEntity();