Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion system/DataCaster/Cast/FloatCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace CodeIgniter\DataCaster\Cast;

use CodeIgniter\DataCaster\Exceptions\CastException;

/**
* Class FloatCast
*
Expand All @@ -30,6 +32,20 @@ public static function get(
self::invalidTypeValueError($value);
}

return (float) $value;
$precision = isset($params[0]) ? (int) $params[0] : null;

if ($precision === null) {
return (float) $value;
}

$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);
}
}
18 changes: 17 additions & 1 deletion system/Entity/Cast/FloatCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,26 @@

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 = 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);
}
}
8 changes: 8 additions & 0 deletions system/Entity/Exceptions/CastException.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,12 @@ 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.
*/
public static function forInvalidFloatRoundingMode(string $mode): static
{
return new static(lang('Cast.invalidFloatRoundingMode', [$mode]));
}
}
29 changes: 15 additions & 14 deletions system/Language/en/Cast.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
'invalidFloatRoundingMode' => 'Invalid rounding mode "{0}" for float casting.',
];
56 changes: 56 additions & 0 deletions tests/system/DataConverter/DataConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
32 changes: 32 additions & 0 deletions tests/system/Entity/EntityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions user_guide_src/source/changelogs/v4.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ Validation
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
***************
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/models/entities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
14 changes: 14 additions & 0 deletions user_guide_src/source/models/model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---

Expand Down
16 changes: 16 additions & 0 deletions user_guide_src/source/models/model/067.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Models;

use CodeIgniter\Model;

class TransactionModel extends Model
{
// ...
protected array $casts = [
'id' => 'int',
'currency' => 'string',
'amount' => 'float[2,even]',
];
// ...
}
Loading