Skip to content

Commit

Permalink
[9.x] Fix decimal cast precision issue (#45456)
Browse files Browse the repository at this point in the history
* add failing test for decimal precision

* fix decimal cast precision with manual string manipulation
  • Loading branch information
timacdonald authored Dec 30, 2022
1 parent 3dff584 commit 39fca5a
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use ReflectionClass;
use ReflectionMethod;
use ReflectionNamedType;
use TypeError;

trait HasAttributes
{
Expand Down Expand Up @@ -1318,7 +1319,15 @@ public function fromFloat($value)
*/
protected function asDecimal($value, $decimals)
{
return number_format($value, $decimals, '.', '');
$value = (string) $value;

if (! is_numeric($value)) {
throw new TypeError('$value must be numeric.');
}

[$int, $fraction] = explode('.', $value) + [1 => ''];

return $int.'.'.Str::of($fraction)->limit($decimals, '')->padLeft($decimals, '0');
}

/**
Expand Down
62 changes: 62 additions & 0 deletions tests/Integration/Database/EloquentModelDecimalCastingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;
use TypeError;

class EloquentModelDecimalCastingTest extends DatabaseTestCase
{
Expand All @@ -18,6 +19,67 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed()
});
}

public function testItThrowsOnNonNumericValues()
{
$model = new class extends Model {
public $timestamps = false;

protected $casts = [
'amount' => 'decimal:20',
];
};
$model->amount = 'foo';

$this->expectException(TypeError::class);

$model->amount;
}

public function testItHandlesLargeNumbers()
{
$model = new class extends Model {
public $timestamps = false;

protected $casts = [
'amount' => 'decimal:20',
];
};

$model->amount = '0.89898989898989898989';
$this->assertSame('0.89898989898989898989', $model->amount);

$model->amount = '89898989898989898989';
$this->assertSame('89898989898989898989.00000000000000000000', $model->amount);
}

public function testItTrimsLongValues()
{
$model = new class extends Model {
public $timestamps = false;

protected $casts = [
'amount' => 'decimal:20',
];
};

$model->amount = '0.89898989898989898989898989898989898989898989';
$this->assertSame('0.89898989898989898989', $model->amount);
}

public function testItDoesntRoundNumbers()
{
$model = new class extends Model {
public $timestamps = false;

protected $casts = [
'amount' => 'decimal:1',
];
};

$model->amount = '0.99';
$this->assertSame('0.9', $model->amount);
}

public function testDecimalsAreCastable()
{
$user = TestModel1::create([
Expand Down

0 comments on commit 39fca5a

Please sign in to comment.