diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index c5f603a28127..7c5378365caf 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Database; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Entity\Entity; use stdClass; @@ -101,17 +102,17 @@ public function __construct(&$connID, &$resultID) * * @param string $type The row type. Either 'array', 'object', or a class name to use */ - public function getResult(string $type = 'object'): array + public function getResult(string $type = 'object', ?DataConverter $converter = null): array { if ($type === 'array') { - return $this->getResultArray(); + return $this->getResultArray($converter); } if ($type === 'object') { - return $this->getResultObject(); + return $this->getResultObject($converter); } - return $this->getCustomResultObject($type); + return $this->getCustomResultObject($type, $converter); } /** @@ -121,7 +122,7 @@ public function getResult(string $type = 'object'): array * * @return array */ - public function getCustomResultObject(string $className) + public function getCustomResultObject(string $className, ?DataConverter $converter = null) { if (isset($this->customResultObject[$className])) { return $this->customResultObject[$className]; @@ -156,7 +157,7 @@ public function getCustomResultObject(string $className) } $this->customResultObject[$className] = []; - while ($row = $this->fetchObject($className)) { + while ($row = $this->fetchObject($className, $converter)) { if (! is_subclass_of($row, Entity::class) && method_exists($row, 'syncOriginal')) { $row->syncOriginal(); } @@ -172,7 +173,7 @@ public function getCustomResultObject(string $className) * * If no results, an empty array is returned. */ - public function getResultArray(): array + public function getResultArray(?DataConverter $converter = null): array { if (! empty($this->resultArray)) { return $this->resultArray; @@ -198,6 +199,10 @@ public function getResultArray(): array } while ($row = $this->fetchAssoc()) { + if ($converter !== null) { + $row = $converter->fromDatabase($row); + } + $this->resultArray[] = $row; } @@ -209,10 +214,9 @@ public function getResultArray(): array * * If no results, an empty array is returned. * - * @return array - * @phpstan-return list + * @return list */ - public function getResultObject(): array + public function getResultObject(?DataConverter $converter = null): array { if (! empty($this->resultObject)) { return $this->resultObject; @@ -237,7 +241,7 @@ public function getResultObject(): array $this->dataSeek(); } - while ($row = $this->fetchObject()) { + while ($row = $this->fetchObject('stdClass', $converter)) { if (! is_subclass_of($row, Entity::class) && method_exists($row, 'syncOriginal')) { $row->syncOriginal(); } @@ -260,12 +264,12 @@ public function getResultObject(): array * @return array|object|stdClass|null * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : object|null)) */ - public function getRow($n = 0, string $type = 'object') + public function getRow($n = 0, string $type = 'object', ?DataConverter $converter = null) { if (! is_numeric($n)) { // We cache the row data for subsequent uses if (! is_array($this->rowData)) { - $this->rowData = $this->getRowArray(); + $this->rowData = $this->getRowArray(0, $converter); } // array_key_exists() instead of isset() to allow for NULL values @@ -277,14 +281,14 @@ public function getRow($n = 0, string $type = 'object') } if ($type === 'object') { - return $this->getRowObject($n); + return $this->getRowObject($n, $converter); } if ($type === 'array') { - return $this->getRowArray($n); + return $this->getRowArray($n, $converter); } - return $this->getCustomRowObject($n, $type); + return $this->getCustomRowObject($n, $type, $converter); } /** @@ -294,10 +298,10 @@ public function getRow($n = 0, string $type = 'object') * * @return array|null */ - public function getCustomRowObject(int $n, string $className) + public function getCustomRowObject(int $n, string $className, ?DataConverter $converter = null) { if (! isset($this->customResultObject[$className])) { - $this->getCustomResultObject($className); + $this->getCustomResultObject($className, $converter); } if (empty($this->customResultObject[$className])) { @@ -318,9 +322,9 @@ public function getCustomRowObject(int $n, string $className) * * @return array|null */ - public function getRowArray(int $n = 0) + public function getRowArray(int $n = 0, ?DataConverter $converter = null) { - $result = $this->getResultArray(); + $result = $this->getResultArray($converter); if (empty($result)) { return null; } @@ -339,9 +343,10 @@ public function getRowArray(int $n = 0) * * @return object|stdClass|null */ - public function getRowObject(int $n = 0) + public function getRowObject(int $n = 0, ?DataConverter $converter = null) { - $result = $this->getResultObject(); + $result = $this->getResultObject($converter); + if (empty($result)) { return null; } @@ -529,5 +534,5 @@ abstract protected function fetchAssoc(); * * @return Entity|false|object|stdClass */ - abstract protected function fetchObject(string $className = 'stdClass'); + abstract protected function fetchObject(string $className = 'stdClass', ?DataConverter $converter = null); } diff --git a/system/Database/DataConverter/DataConverter.php b/system/Database/DataConverter/DataConverter.php index 1f998052622f..36b1bc20f7a5 100644 --- a/system/Database/DataConverter/DataConverter.php +++ b/system/Database/DataConverter/DataConverter.php @@ -26,6 +26,8 @@ /** * PHP data <==> DB data converter + * + * @see \CodeIgniter\Database\DataConverter\DataConverterTest */ class DataConverter { diff --git a/system/Database/MySQLi/Result.php b/system/Database/MySQLi/Result.php index 578169deb757..2bb5fc38481d 100644 --- a/system/Database/MySQLi/Result.php +++ b/system/Database/MySQLi/Result.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Database\MySQLi; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Entity\Entity; use mysqli; use mysqli_result; @@ -145,7 +146,7 @@ protected function fetchAssoc() * * @return Entity|false|object|stdClass */ - protected function fetchObject(string $className = 'stdClass') + protected function fetchObject(string $className = 'stdClass', ?DataConverter $converter = null) { if (is_subclass_of($className, Entity::class)) { return empty($data = $this->fetchAssoc()) ? false : (new $className())->injectRawData($data); diff --git a/system/Database/OCI8/Result.php b/system/Database/OCI8/Result.php index 01025763d423..62b26c625640 100644 --- a/system/Database/OCI8/Result.php +++ b/system/Database/OCI8/Result.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Database\OCI8; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Entity\Entity; use stdClass; @@ -95,7 +96,7 @@ protected function fetchAssoc() * * @return Entity|false|object|stdClass */ - protected function fetchObject(string $className = 'stdClass') + protected function fetchObject(string $className = 'stdClass', ?DataConverter $converter = null) { $row = oci_fetch_object($this->resultID); diff --git a/system/Database/Postgre/Result.php b/system/Database/Postgre/Result.php index 0a828757632e..ce54853e5e07 100644 --- a/system/Database/Postgre/Result.php +++ b/system/Database/Postgre/Result.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Database\Postgre; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Entity\Entity; use PgSql\Connection as PgSqlConnection; use PgSql\Result as PgSqlResult; @@ -111,7 +112,7 @@ protected function fetchAssoc() * * @return Entity|false|object|stdClass */ - protected function fetchObject(string $className = 'stdClass') + protected function fetchObject(string $className = 'stdClass', ?DataConverter $converter = null) { if (is_subclass_of($className, Entity::class)) { return empty($data = $this->fetchAssoc()) ? false : (new $className())->injectRawData($data); diff --git a/system/Database/SQLSRV/Result.php b/system/Database/SQLSRV/Result.php index f245a01669c6..ac186c995c7f 100755 --- a/system/Database/SQLSRV/Result.php +++ b/system/Database/SQLSRV/Result.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Database\SQLSRV; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Entity\Entity; use stdClass; @@ -151,7 +152,7 @@ protected function fetchAssoc() * * @return Entity|false|object|stdClass */ - protected function fetchObject(string $className = 'stdClass') + protected function fetchObject(string $className = 'stdClass', ?DataConverter $converter = null) { if (is_subclass_of($className, Entity::class)) { return empty($data = $this->fetchAssoc()) ? false : (new $className())->injectRawData($data); diff --git a/system/Database/SQLite3/Result.php b/system/Database/SQLite3/Result.php index f3887b044b49..e3faa3cca0fc 100644 --- a/system/Database/SQLite3/Result.php +++ b/system/Database/SQLite3/Result.php @@ -13,6 +13,7 @@ use Closure; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Entity\Entity; use SQLite3; @@ -128,13 +129,17 @@ protected function fetchAssoc() * * @return Entity|false|object|stdClass */ - protected function fetchObject(string $className = 'stdClass') + protected function fetchObject(string $className = 'stdClass', ?DataConverter $converter = null) { // No native support for fetching rows as objects if (($row = $this->fetchAssoc()) === false) { return false; } + if ($converter !== null) { + $row = $converter->fromDatabase($row); + } + if ($className === 'stdClass') { return (object) $row; } diff --git a/system/Test/Mock/MockResult.php b/system/Test/Mock/MockResult.php index 87654b1dd779..57eb392f0662 100644 --- a/system/Test/Mock/MockResult.php +++ b/system/Test/Mock/MockResult.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Test\Mock; use CodeIgniter\Database\BaseResult; +use CodeIgniter\Database\DataConverter\DataConverter; use stdClass; /** @@ -86,7 +87,7 @@ protected function fetchAssoc() * * @return object|stdClass */ - protected function fetchObject($className = 'stdClass') + protected function fetchObject($className = 'stdClass', ?DataConverter $converter = null) { return new $className(); } diff --git a/tests/system/Database/Live/DataConverterTest.php b/tests/system/Database/Live/DataConverterTest.php new file mode 100644 index 000000000000..1d6660428c5f --- /dev/null +++ b/tests/system/Database/Live/DataConverterTest.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\Live; + +use CodeIgniter\Database\DataConverter\DataConverter; +use CodeIgniter\I18n\Time; +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\DatabaseTestTrait; +use stdClass; +use Tests\Support\Database\Seeds\CITestSeeder; + +/** + * @group DatabaseLive + * + * @internal + */ +final class DataConverterTest extends CIUnitTestCase +{ + use DatabaseTestTrait; + + protected $refresh = true; + protected $seed = CITestSeeder::class; + + public function testGetRowArrayDatetime() + { + $converter = new DataConverter([ + 'type_datetime' => 'datetime', + ]); + + $data = $this->db->table('type_test') + ->get() + ->getRow(0, 'array', $converter); + + $this->assertInstanceOf(Time::class, $data['type_datetime']); + } + + public function testGetRowObjectDatetime() + { + $converter = new DataConverter([ + 'type_datetime' => 'datetime', + ]); + + $data = $this->db->table('type_test') + ->get() + ->getRow(0, 'object', $converter); + + $this->assertInstanceOf(Time::class, $data->type_datetime); + } + + public function testGetRowCustomObjectDatetime() + { + $converter = new DataConverter([ + 'type_datetime' => 'datetime', + ]); + + $data = $this->db->table('type_test') + ->get() + ->getRow(0, CustomStdClass::class, $converter); + + $this->assertInstanceOf(Time::class, $data->type_datetime); + } + + public function testGetResultArrayDatetime() + { + $converter = new DataConverter([ + 'type_datetime' => 'datetime', + ]); + + $data = $this->db->table('type_test') + ->get() + ->getResult('array', $converter); + + $this->assertInstanceOf(Time::class, $data[0]['type_datetime']); + } + + public function testGetResulObjectDatetime() + { + $converter = new DataConverter([ + 'type_datetime' => 'datetime', + ]); + + $data = $this->db->table('type_test') + ->get() + ->getResult('object', $converter); + + $this->assertInstanceOf(Time::class, $data[0]->type_datetime); + } + + public function testGetResultCustomObjectDatetime() + { + $converter = new DataConverter([ + 'type_datetime' => 'datetime', + ]); + + $data = $this->db->table('type_test') + ->get() + ->getResult(CustomStdClass::class, $converter); + + $this->assertInstanceOf(Time::class, $data[0]->type_datetime); + } +} + +class CustomStdClass extends stdClass +{ +} diff --git a/tests/system/View/TableTest.php b/tests/system/View/TableTest.php index f4d7f4056f2d..3829239b4e34 100644 --- a/tests/system/View/TableTest.php +++ b/tests/system/View/TableTest.php @@ -11,6 +11,7 @@ namespace CodeIgniter\View; +use CodeIgniter\Database\DataConverter\DataConverter; use CodeIgniter\Database\MySQLi\Result; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\Mock\MockTable; @@ -887,7 +888,7 @@ public function getFieldNames(): array ]; } - public function getResultArray(): array + public function getResultArray(?DataConverter $converter = null): array { return [ [