From 1e9438baf529693a667bc66b926b069a54688718 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 6 Apr 2026 14:55:02 +0530 Subject: [PATCH 1/5] Added precision and rounding mode in FloatCast --- system/DataCaster/Cast/FloatCast.php | 29 +++++++++- system/Entity/Exceptions/CastException.php | 10 ++++ .../DataConverter/DataConverterTest.php | 56 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/system/DataCaster/Cast/FloatCast.php b/system/DataCaster/Cast/FloatCast.php index d2173826265a..279fecaecc13 100644 --- a/system/DataCaster/Cast/FloatCast.php +++ b/system/DataCaster/Cast/FloatCast.php @@ -13,6 +13,8 @@ namespace CodeIgniter\DataCaster\Cast; +use CodeIgniter\DataCaster\Exceptions\CastException; + /** * Class FloatCast * @@ -30,6 +32,31 @@ public static function get( self::invalidTypeValueError($value); } - return (float) $value; + $precision = isset($params[0]) ? (int) $params[0] : null; + + if ($precision === null) { + return (float) $value; + } + + // Map string flags to PHP constants + $modeMap = [ + 'up' => PHP_ROUND_HALF_UP, + 'down' => PHP_ROUND_HALF_DOWN, + 'even' => PHP_ROUND_HALF_EVEN, + 'odd' => PHP_ROUND_HALF_ODD, + ]; + + $mode = PHP_ROUND_HALF_UP; // Default mode + + if (isset($params[1])) { + $modeParam = strtolower($params[1]); + if (isset($modeMap[$modeParam])) { + $mode = $modeMap[$modeParam]; + } else { + throw CastException::forInvalidFloatRoundingMode($params[1]); + } + } + + return round((float) $value, $precision, $mode); } } diff --git a/system/Entity/Exceptions/CastException.php b/system/Entity/Exceptions/CastException.php index 033f1ced478e..3bf48a2b1d05 100644 --- a/system/Entity/Exceptions/CastException.php +++ b/system/Entity/Exceptions/CastException.php @@ -122,4 +122,14 @@ public static function forInvalidEnumType(string $expectedClass, string $actualC { return new static(lang('Cast.enumInvalidType', [$actualClass, $expectedClass])); } + + /** + * Thrown when an invalid rounding mode is provided for float casting. + * + * @return static + */ + public static function forInvalidFloatRoundingMode(string $mode) + { + return new static(lang('Cast.floatInvalidRoundingMode', [$mode])); + } } diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index 220e101ba762..e5e25ce60225 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -197,6 +197,62 @@ public static function provideConvertDataFromDB(): iterable 'temp' => 15.9, ], ], + 'float precise' => [ + [ + 'id' => 'int', + 'temp' => 'float[2]', + ], + [ + 'id' => '1', + 'temp' => '15.98765', + ], + [ + 'id' => 1, + 'temp' => 15.99, + ] + ], + 'float precise-down' => [ + [ + 'id' => 'int', + 'temp' => 'float[2,down]', + ], + [ + 'id' => '1', + 'temp' => '1.235', + ], + [ + 'id' => 1, + 'temp' => 1.23, + ] + ], + 'float precise-even' => [ + [ + 'id' => 'int', + 'temp' => 'float[2,even]', + ], + [ + 'id' => '1', + 'temp' => '20.005', + ], + [ + 'id' => 1, + 'temp' => 20.00, + ] + ], + 'float precise-odd' => [ + [ + 'id' => 'int', + 'temp' => 'float[2,odd]', + ], + [ + 'id' => '1', + 'temp' => '1.255', + ], + [ + 'id' => 1, + 'temp' => 1.25, + ] + ], 'enum string-backed' => [ [ 'id' => 'int', From 4f021b68a271ba1e80f20af84c1d89e3a5048729 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 6 Apr 2026 15:00:21 +0530 Subject: [PATCH 2/5] cs-fix --- tests/system/DataConverter/DataConverterTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index e5e25ce60225..9e4bcc49c25b 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -209,7 +209,7 @@ public static function provideConvertDataFromDB(): iterable [ 'id' => 1, 'temp' => 15.99, - ] + ], ], 'float precise-down' => [ [ @@ -223,7 +223,7 @@ public static function provideConvertDataFromDB(): iterable [ 'id' => 1, 'temp' => 1.23, - ] + ], ], 'float precise-even' => [ [ @@ -237,7 +237,7 @@ public static function provideConvertDataFromDB(): iterable [ 'id' => 1, 'temp' => 20.00, - ] + ], ], 'float precise-odd' => [ [ @@ -251,7 +251,7 @@ public static function provideConvertDataFromDB(): iterable [ 'id' => 1, 'temp' => 1.25, - ] + ], ], 'enum string-backed' => [ [ From 9251f96a8daad2c8e123c3e5df3c2acaeb582a45 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 8 Apr 2026 12:35:03 +0530 Subject: [PATCH 3/5] Apply suggested changes and added language key and docs --- system/DataCaster/Cast/FloatCast.php | 21 +++++---------- system/Entity/Cast/FloatCast.php | 22 +++++++++++++++- system/Language/en/Cast.php | 29 +++++++++++---------- user_guide_src/source/changelogs/v4.8.0.rst | 5 ++++ user_guide_src/source/models/entities.rst | 1 + 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/system/DataCaster/Cast/FloatCast.php b/system/DataCaster/Cast/FloatCast.php index 279fecaecc13..56bf65379c97 100644 --- a/system/DataCaster/Cast/FloatCast.php +++ b/system/DataCaster/Cast/FloatCast.php @@ -38,23 +38,16 @@ public static function get( return (float) $value; } - // Map string flags to PHP constants - $modeMap = [ - 'up' => PHP_ROUND_HALF_UP, - 'down' => PHP_ROUND_HALF_DOWN, - 'even' => PHP_ROUND_HALF_EVEN, - 'odd' => PHP_ROUND_HALF_ODD, - ]; - $mode = PHP_ROUND_HALF_UP; // Default mode if (isset($params[1])) { - $modeParam = strtolower($params[1]); - if (isset($modeMap[$modeParam])) { - $mode = $modeMap[$modeParam]; - } else { - throw CastException::forInvalidFloatRoundingMode($params[1]); - } + $mode = match (strtolower($params[1])) { + 'up' => PHP_ROUND_HALF_UP, + 'down' => PHP_ROUND_HALF_DOWN, + 'even' => PHP_ROUND_HALF_EVEN, + 'odd' => PHP_ROUND_HALF_ODD, + default => throw CastException::forInvalidFloatRoundingMode($params[1]), + }; } return round((float) $value, $precision, $mode); diff --git a/system/Entity/Cast/FloatCast.php b/system/Entity/Cast/FloatCast.php index 1a767c0953f0..16256f7938fd 100644 --- a/system/Entity/Cast/FloatCast.php +++ b/system/Entity/Cast/FloatCast.php @@ -13,10 +13,30 @@ namespace CodeIgniter\Entity\Cast; +use CodeIgniter\DataCaster\Exceptions\CastException; + class FloatCast extends BaseCast { public static function get($value, array $params = []): float { - return (float) $value; + $precision = isset($params[0]) ? (int) $params[0] : null; + + if ($precision === null) { + return (float) $value; + } + + $mode = PHP_ROUND_HALF_UP; // Default mode + + if (isset($params[1])) { + $mode = match (strtolower($params[1])) { + 'up' => PHP_ROUND_HALF_UP, + 'down' => PHP_ROUND_HALF_DOWN, + 'even' => PHP_ROUND_HALF_EVEN, + 'odd' => PHP_ROUND_HALF_ODD, + default => throw CastException::forInvalidFloatRoundingMode($params[1]), + }; + } + + return round((float) $value, $precision, $mode); } } diff --git a/system/Language/en/Cast.php b/system/Language/en/Cast.php index 63d9fba01b7c..f9876801cbf9 100644 --- a/system/Language/en/Cast.php +++ b/system/Language/en/Cast.php @@ -13,18 +13,19 @@ // Cast language settings return [ - 'baseCastMissing' => 'The "{0}" class must inherit the "CodeIgniter\Entity\Cast\BaseCast" class.', - 'enumInvalidCaseName' => 'Invalid case name "{0}" for enum "{1}".', - 'enumInvalidType' => 'Expected enum of type "{1}", but received "{0}".', - 'enumInvalidValue' => 'Invalid value "{1}" for enum "{0}".', - 'enumMissingClass' => 'Enum class must be specified for enum casting.', - 'enumNotEnum' => 'The "{0}" is not a valid enum class.', - 'invalidCastMethod' => 'The "{0}" is invalid cast method, valid methods are: ["get", "set"].', - 'invalidTimestamp' => 'Type casting "timestamp" expects a correct timestamp.', - 'jsonErrorCtrlChar' => 'Unexpected control character found.', - 'jsonErrorDepth' => 'Maximum stack depth exceeded.', - 'jsonErrorStateMismatch' => 'Underflow or the modes mismatch.', - 'jsonErrorSyntax' => 'Syntax error, malformed JSON.', - 'jsonErrorUnknown' => 'Unknown error.', - 'jsonErrorUtf8' => 'Malformed UTF-8 characters, possibly incorrectly encoded.', + 'baseCastMissing' => 'The "{0}" class must inherit the "CodeIgniter\Entity\Cast\BaseCast" class.', + 'enumInvalidCaseName' => 'Invalid case name "{0}" for enum "{1}".', + 'enumInvalidType' => 'Expected enum of type "{1}", but received "{0}".', + 'enumInvalidValue' => 'Invalid value "{1}" for enum "{0}".', + 'enumMissingClass' => 'Enum class must be specified for enum casting.', + 'enumNotEnum' => 'The "{0}" is not a valid enum class.', + 'invalidCastMethod' => 'The "{0}" is invalid cast method, valid methods are: ["get", "set"].', + 'invalidTimestamp' => 'Type casting "timestamp" expects a correct timestamp.', + 'jsonErrorCtrlChar' => 'Unexpected control character found.', + 'jsonErrorDepth' => 'Maximum stack depth exceeded.', + 'jsonErrorStateMismatch' => 'Underflow or the modes mismatch.', + 'jsonErrorSyntax' => 'Syntax error, malformed JSON.', + 'jsonErrorUnknown' => 'Unknown error.', + 'jsonErrorUtf8' => 'Malformed UTF-8 characters, possibly incorrectly encoded.', + 'floatInvalidRoundingMode' => 'Invalid rounding mode "{0}" for float casting.', ]; diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index e026fdadaa11..33b337d63306 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -246,6 +246,11 @@ Validation - Custom rule methods that set an error via the ``&$error`` reference parameter now support the ``{field}``, ``{param}``, and ``{value}`` placeholders, consistent with language-file and ``setRule()``/``setRules()`` error messages. +Entities +======== + +- **Float and Double Casting:** Added support for precision and rounding mode when casting to float or double in entities. + Others ====== diff --git a/user_guide_src/source/models/entities.rst b/user_guide_src/source/models/entities.rst index 56c17114138e..f31c2981ba87 100644 --- a/user_guide_src/source/models/entities.rst +++ b/user_guide_src/source/models/entities.rst @@ -261,6 +261,7 @@ Add a question mark at the beginning of type to mark property as nullable, i.e., .. note:: **int-bool** can be used since v4.3.0. .. note:: **enum** can be used since v4.7.0. +.. note:: Since v4.8.0, you can also pass parameters to **float** and **double** types to specify the number of decimal places and rounding mode, i.e., **float[2,even]**. For example, if you had a User entity with an ``is_banned`` property, you can cast it as a boolean: From b520cd4f9a7907d2b4a45186cff77c00c245f56f Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sun, 12 Apr 2026 09:26:36 +0530 Subject: [PATCH 4/5] Apply suggested changes --- system/DataCaster/Cast/FloatCast.php | 18 +++++------- system/Entity/Cast/FloatCast.php | 18 +++++------- system/Entity/Exceptions/CastException.php | 6 ++-- system/Language/en/Cast.php | 2 +- tests/system/Entity/EntityTest.php | 32 +++++++++++++++++++++ user_guide_src/source/changelogs/v4.8.0.rst | 8 ++---- user_guide_src/source/models/model.rst | 14 +++++++++ user_guide_src/source/models/model/067.php | 16 +++++++++++ 8 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 user_guide_src/source/models/model/067.php diff --git a/system/DataCaster/Cast/FloatCast.php b/system/DataCaster/Cast/FloatCast.php index 56bf65379c97..858ff7ce7aac 100644 --- a/system/DataCaster/Cast/FloatCast.php +++ b/system/DataCaster/Cast/FloatCast.php @@ -38,17 +38,13 @@ public static function get( return (float) $value; } - $mode = PHP_ROUND_HALF_UP; // Default mode - - if (isset($params[1])) { - $mode = match (strtolower($params[1])) { - 'up' => PHP_ROUND_HALF_UP, - 'down' => PHP_ROUND_HALF_DOWN, - 'even' => PHP_ROUND_HALF_EVEN, - 'odd' => PHP_ROUND_HALF_ODD, - default => throw CastException::forInvalidFloatRoundingMode($params[1]), - }; - } + $mode = match (strtolower($params[1] ?? 'up')) { + 'up' => PHP_ROUND_HALF_UP, + 'down' => PHP_ROUND_HALF_DOWN, + 'even' => PHP_ROUND_HALF_EVEN, + 'odd' => PHP_ROUND_HALF_ODD, + default => throw CastException::forInvalidFloatRoundingMode($params[1]), + }; return round((float) $value, $precision, $mode); } diff --git a/system/Entity/Cast/FloatCast.php b/system/Entity/Cast/FloatCast.php index 16256f7938fd..e2fc12b9ec45 100644 --- a/system/Entity/Cast/FloatCast.php +++ b/system/Entity/Cast/FloatCast.php @@ -25,17 +25,13 @@ public static function get($value, array $params = []): float return (float) $value; } - $mode = PHP_ROUND_HALF_UP; // Default mode - - if (isset($params[1])) { - $mode = match (strtolower($params[1])) { - 'up' => PHP_ROUND_HALF_UP, - 'down' => PHP_ROUND_HALF_DOWN, - 'even' => PHP_ROUND_HALF_EVEN, - 'odd' => PHP_ROUND_HALF_ODD, - default => throw CastException::forInvalidFloatRoundingMode($params[1]), - }; - } + $mode = match (strtolower($params[1] ?? 'up')) { + 'up' => PHP_ROUND_HALF_UP, + 'down' => PHP_ROUND_HALF_DOWN, + 'even' => PHP_ROUND_HALF_EVEN, + 'odd' => PHP_ROUND_HALF_ODD, + default => throw CastException::forInvalidFloatRoundingMode($params[1]), + }; return round((float) $value, $precision, $mode); } diff --git a/system/Entity/Exceptions/CastException.php b/system/Entity/Exceptions/CastException.php index 3bf48a2b1d05..252eb0c42df2 100644 --- a/system/Entity/Exceptions/CastException.php +++ b/system/Entity/Exceptions/CastException.php @@ -125,11 +125,9 @@ public static function forInvalidEnumType(string $expectedClass, string $actualC /** * Thrown when an invalid rounding mode is provided for float casting. - * - * @return static */ - public static function forInvalidFloatRoundingMode(string $mode) + public static function forInvalidFloatRoundingMode(string $mode): static { - return new static(lang('Cast.floatInvalidRoundingMode', [$mode])); + return new static(lang('Cast.invalidFloatRoundingMode', [$mode])); } } diff --git a/system/Language/en/Cast.php b/system/Language/en/Cast.php index f9876801cbf9..d4762634ca81 100644 --- a/system/Language/en/Cast.php +++ b/system/Language/en/Cast.php @@ -27,5 +27,5 @@ 'jsonErrorSyntax' => 'Syntax error, malformed JSON.', 'jsonErrorUnknown' => 'Unknown error.', 'jsonErrorUtf8' => 'Malformed UTF-8 characters, possibly incorrectly encoded.', - 'floatInvalidRoundingMode' => 'Invalid rounding mode "{0}" for float casting.', + 'invalidFloatRoundingMode' => 'Invalid rounding mode "{0}" for float casting.', ]; diff --git a/tests/system/Entity/EntityTest.php b/tests/system/Entity/EntityTest.php index 7c19d9d09b89..2d5ed2671f1a 100644 --- a/tests/system/Entity/EntityTest.php +++ b/tests/system/Entity/EntityTest.php @@ -419,6 +419,36 @@ public function testCastFloat(): void $this->assertEqualsWithDelta(3.6, $entity->second, PHP_FLOAT_EPSILON); } + public function testCastFloatWithPrecision(): void + { + $entity = $this->getCastEntity(); + + $entity->fourteenth = 3.1415926535; + + $this->assertIsFloat($entity->fourteenth); + $this->assertEqualsWithDelta(3.14, $entity->fourteenth, PHP_FLOAT_EPSILON); + + $entity->fourteenth = '3.1415926535'; + + $this->assertIsFloat($entity->fourteenth); + $this->assertEqualsWithDelta(3.14, $entity->fourteenth, PHP_FLOAT_EPSILON); + } + + public function testCastFloatWithPrecisionAndRoundingMode(): void + { + $entity = $this->getCastEntity(); + + $entity->fifteenth = 3.145; + + $this->assertIsFloat($entity->fifteenth); + $this->assertEqualsWithDelta(3.14, $entity->fifteenth, PHP_FLOAT_EPSILON); + + $entity->fifteenth = '3.135'; + + $this->assertIsFloat($entity->fifteenth); + $this->assertEqualsWithDelta(3.13, $entity->fifteenth, PHP_FLOAT_EPSILON); + } + public function testCastDouble(): void { $entity = $this->getCastEntity(); @@ -1750,6 +1780,8 @@ private function getCastEntity($data = null): object 'eleventh' => 'json-array', 'twelfth' => 'csv', 'thirteenth' => 'uri', + 'fourteenth' => 'float[2]', + 'fifteenth' => 'float[2,down]', ]; public function setSeventh(string $seventh): void diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 33b337d63306..99c0adfc8355 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -246,14 +246,12 @@ Validation - Custom rule methods that set an error via the ``&$error`` reference parameter now support the ``{field}``, ``{param}``, and ``{value}`` placeholders, consistent with language-file and ``setRule()``/``setRules()`` error messages. -Entities -======== - -- **Float and Double Casting:** Added support for precision and rounding mode when casting to float or double in entities. - Others ====== +- **Float and Double Casting:** Added support for precision and rounding mode when casting to float or double in entities. +- Float and Double casting now throws ``CastException::forInvalidFloatRoundingMode()`` if an rounding mode other than up, down, even or odd is provided. + *************** Message Changes *************** diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index beab1d17c5c7..800ae304f117 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -380,6 +380,20 @@ of type to mark the field as nullable, i.e., ``?int``, ``?datetime``. |``enum`` | Enum | string/int type | +---------------+----------------+---------------------------+ +float +----- + +Casting as ``float`` will convert the value to a float type in PHP. +This is best used with database columns that are of a float or numeric type. + +You can also pass arguments to the ``float`` type to specify the number +of decimal places to round to as well as the rounding mode (up, down, even or odd). + +.. literalinclude:: model/067.php + +.. note:: Prior to v4.8.0 the ``float`` type did not support any parameters. + It simply converted the value to a float type in PHP without rounding. + csv --- diff --git a/user_guide_src/source/models/model/067.php b/user_guide_src/source/models/model/067.php new file mode 100644 index 000000000000..969d130e44aa --- /dev/null +++ b/user_guide_src/source/models/model/067.php @@ -0,0 +1,16 @@ + 'int', + 'currency' => 'string', + 'amount' => 'float[2,even]', + ]; + // ... +} From 5c7d79e987a582bda7c10e288acc75101aa97617 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sun, 12 Apr 2026 09:26:54 +0530 Subject: [PATCH 5/5] cs-fix --- user_guide_src/source/models/model/067.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/models/model/067.php b/user_guide_src/source/models/model/067.php index 969d130e44aa..128fe1313956 100644 --- a/user_guide_src/source/models/model/067.php +++ b/user_guide_src/source/models/model/067.php @@ -8,9 +8,9 @@ class TransactionModel extends Model { // ... protected array $casts = [ - 'id' => 'int', - 'currency' => 'string', - 'amount' => 'float[2,even]', + 'id' => 'int', + 'currency' => 'string', + 'amount' => 'float[2,even]', ]; // ... }