From fd32a8fcd9fdca38ced519977898a2290cf0d9c1 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 6 Apr 2026 14:55:02 +0530 Subject: [PATCH 1/3] 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 d0717fc15c5f37a174ecbe477fc1c8af28deef33 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 6 Apr 2026 15:00:21 +0530 Subject: [PATCH 2/3] 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 31d2b6fc8971fba9f6ad0c501a367f99fd3d3235 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 8 Apr 2026 12:35:03 +0530 Subject: [PATCH 3/3] 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 045c38f11ce5..5c4e64d1c7cf 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -245,6 +245,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: