From 24ad60eb23cf5d2dc5d1bf002f2a5440ddfea3e1 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 31 May 2026 02:00:41 +0200 Subject: [PATCH 1/3] fix: pass $recursive parameter to parent in Model::objectToRawArray --- system/Model.php | 2 +- .../Models/ObjectToRawArrayModelTest.php | 129 ++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 tests/system/Models/ObjectToRawArrayModelTest.php diff --git a/system/Model.php b/system/Model.php index f4ddd75276d3..0e1cf9da9c3b 100644 --- a/system/Model.php +++ b/system/Model.php @@ -713,7 +713,7 @@ public function update($id = null, $row = null): bool protected function objectToRawArray($object, bool $onlyChanged = true, bool $recursive = false): array { - return parent::objectToRawArray($object, $onlyChanged); + return parent::objectToRawArray($object, $onlyChanged, $recursive); } /** diff --git a/tests/system/Models/ObjectToRawArrayModelTest.php b/tests/system/Models/ObjectToRawArrayModelTest.php new file mode 100644 index 000000000000..7ea2ad24dcf2 --- /dev/null +++ b/tests/system/Models/ObjectToRawArrayModelTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Models; + +use CodeIgniter\Entity\Entity; +use CodeIgniter\Model; +use CodeIgniter\Test\CIUnitTestCase; +use PHPUnit\Framework\Attributes\Group; +use ReflectionMethod; + +/** + * @internal + */ +#[Group('Models')] +final class ObjectToRawArrayModelTest extends CIUnitTestCase +{ + private function createModel(): Model + { + return new class () extends Model { + public function __construct() + { + // Skip DB connection — we only test objectToRawArray + } + + protected $table = 'test'; + protected $allowedFields = ['name', 'nested', 'entity']; + protected $returnType = 'array'; + protected $useSoftDeletes = false; + }; + } + + /** + * Call protected objectToRawArray via reflection. + */ + private function callObjectToRawArray(Model $model, object $object, bool $onlyChanged, bool $recursive): array + { + $method = new ReflectionMethod(Model::class, 'objectToRawArray'); + + return $method->invoke($model, $object, $onlyChanged, $recursive); + } + + public function testObjectToRawArrayPassesRecursiveTrue(): void + { + $model = $this->createModel(); + + $inner = new class () extends Entity { + protected $attributes = ['name' => 'inner']; + protected $original = ['name' => 'inner']; + }; + + $outer = new class () extends Entity { + protected $attributes = ['name' => 'outer', 'nested' => null]; + protected $original = ['name' => 'outer', 'nested' => null]; + }; + $outer->nested = $inner; + + $result = $this->callObjectToRawArray($model, $outer, false, true); + + $this->assertArrayHasKey('name', $result); + $this->assertSame('outer', $result['name']); + $this->assertArrayHasKey('nested', $result); + $this->assertIsArray($result['nested']); + $this->assertSame(['name' => 'inner'], $result['nested']); + } + + public function testObjectToRawArrayPassesRecursiveFalse(): void + { + $model = $this->createModel(); + + $inner = new class () extends Entity { + protected $attributes = ['name' => 'inner']; + protected $original = ['name' => 'inner']; + }; + + $outer = new class () extends Entity { + protected $attributes = ['name' => 'outer', 'nested' => null]; + protected $original = ['name' => 'outer', 'nested' => null]; + }; + $outer->nested = $inner; + + $result = $this->callObjectToRawArray($model, $outer, false, false); + + $this->assertArrayHasKey('name', $result); + $this->assertSame('outer', $result['name']); + $this->assertArrayHasKey('nested', $result); + // With recursive=false, nested Entity should remain as object + $this->assertInstanceOf(Entity::class, $result['nested']); + } + + public function testObjectToRawArrayNonEntity(): void + { + $model = $this->createModel(); + + $obj = new class () { + public string $name = 'test'; + public string $value = '123'; + }; + + $result = $this->callObjectToRawArray($model, $obj, false, false); + + $this->assertSame(['name' => 'test', 'value' => '123'], $result); + } + + public function testObjectToRawArrayOnlyChanged(): void + { + $model = $this->createModel(); + + $entity = new class () extends Entity { + protected $attributes = ['name' => 'original', 'value' => 'keep']; + protected $original = ['name' => 'original', 'value' => 'keep']; + }; + $entity->name = 'modified'; + + $result = $this->callObjectToRawArray($model, $entity, true, false); + + $this->assertSame(['name' => 'modified'], $result); + } +} From 26bcef97b4bf1fb3883d44f13b6e9a40e072beee Mon Sep 17 00:00:00 2001 From: Bogdan Lambarski Date: Mon, 1 Jun 2026 21:51:27 +0200 Subject: [PATCH 2/3] Update tests/system/Models/ObjectToRawArrayModelTest.php Co-authored-by: Michal Sniatala --- tests/system/Models/ObjectToRawArrayModelTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Models/ObjectToRawArrayModelTest.php b/tests/system/Models/ObjectToRawArrayModelTest.php index 7ea2ad24dcf2..062504063b48 100644 --- a/tests/system/Models/ObjectToRawArrayModelTest.php +++ b/tests/system/Models/ObjectToRawArrayModelTest.php @@ -45,9 +45,9 @@ public function __construct() */ private function callObjectToRawArray(Model $model, object $object, bool $onlyChanged, bool $recursive): array { - $method = new ReflectionMethod(Model::class, 'objectToRawArray'); + $method = self::getPrivateMethodInvoker($model, 'objectToRawArray'); - return $method->invoke($model, $object, $onlyChanged, $recursive); + return $method($object, $onlyChanged, $recursive); } public function testObjectToRawArrayPassesRecursiveTrue(): void From f63d78f7d814dfdf7e1d0893d22480052c6eeeb7 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Mon, 1 Jun 2026 22:05:03 +0200 Subject: [PATCH 3/3] fix: remove redundant objectToRawArray method and update changelog --- system/Model.php | 5 ----- user_guide_src/source/changelogs/v4.7.4.rst | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/system/Model.php b/system/Model.php index 0e1cf9da9c3b..3599ba963176 100644 --- a/system/Model.php +++ b/system/Model.php @@ -711,11 +711,6 @@ public function update($id = null, $row = null): bool return parent::update($id, $row); } - protected function objectToRawArray($object, bool $onlyChanged = true, bool $recursive = false): array - { - return parent::objectToRawArray($object, $onlyChanged, $recursive); - } - /** * Provides/instantiates the builder/db connection and model's table/primary key names and return type. * diff --git a/user_guide_src/source/changelogs/v4.7.4.rst b/user_guide_src/source/changelogs/v4.7.4.rst index daf64bbda631..61eac41e0b28 100644 --- a/user_guide_src/source/changelogs/v4.7.4.rst +++ b/user_guide_src/source/changelogs/v4.7.4.rst @@ -32,6 +32,7 @@ Bugs Fixed - **Database:** Fixed a bug where ``updateBatch()`` could be called after Query Builder ``where()`` conditions, even though it's not supported. In this situation, now the ``DatabaseException`` is thrown. - **HTTP:** Fixed a bug where the User Agent library reported Safari's WebKit version instead of the browser version from the ``Version`` token. +- **Model:** Fixed a bug in ``Model::objectToRawArray()`` where the ``$recursive`` parameter was ignored. See the repo's `CHANGELOG.md `_