Skip to content

Commit

Permalink
feat(Database): Add ability to force null values to 0.0 when using Fl…
Browse files Browse the repository at this point in the history
…oatCast

By using: FloatCast::NonNull, FloatCast::OneDecimalNonNull, FloatCast::ThreeDecimalsNonNull,
FloatCast::FourDecimalsNonNull
  • Loading branch information
pionl committed Jul 24, 2023
1 parent 11461dd commit e99a21e
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 7 deletions.
13 changes: 9 additions & 4 deletions src/Database/Models/Casts/FloatCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@ final class FloatCast implements CastsAttributes
{
// Laravel casts supports creating cast with arguments.
public const OneDecimal = self::class . ':1';
public const OneDecimalNonNull = self::class . ':1,true';
public const ThreeDecimals = self::class . ':3';
public const ThreeDecimalsNonNull = self::class . ':3,true';
public const FourDecimals = self::class . ':4';
public const FourDecimalsNonNull = self::class . ':4,true';
public const NonNull = self::class . ':2,true';

public function __construct(
private readonly int $decimals = 2
private readonly int $decimals = 2,
private readonly bool $nonNull = false
) {
}

public function get($model, string $key, $value, array $attributes)
{
if ($value === null || $value === '') {
return null;
return $this->nonNull ? 0.0 : null;
}

return (float) $value;
Expand All @@ -35,11 +40,11 @@ public function set($model, string $key, $value, array $attributes): ?string
{
$floatVal = Value::toFloat((string) $value);

if ($floatVal === null) {
if ($floatVal === null && $this->nonNull === false) {
return null;
}

// Simulate value from database in DECIMAL format.
return number_format($floatVal, $this->decimals, '.', '');
return number_format($floatVal ?? 0.0, $this->decimals, '.', '');
}
}
93 changes: 90 additions & 3 deletions tests/Feature/Database/Models/Casts/FloatCastTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ public function dataEnsureThatFloatIsReturned(): array
'empty string' => [
static fn (self $self) => $self->assertEnsureThatFloatIsReturned(value: '', expected: null),
],
'non null set to true, null' => [
static fn (self $self) => $self->assertEnsureThatFloatIsReturned(
value: null,
expected: 0.0,
nonNull: true
),
],
'non null set to true, empty string' => [
static fn (self $self) => $self->assertEnsureThatFloatIsReturned(
value: '',
expected: 0.0,
nonNull: true
),
],
'non null set to true, decimals' => [
static fn (self $self) => $self->assertEnsureThatFloatIsReturned(
value: '123.00',
expected: 123.0,
nonNull: true,
),
],
'non null set to true, decimals - long' => [
static fn (self $self) => $self->assertEnsureThatFloatIsReturned(
value: '123.0002',
expected: 123.0002,
nonNull: true,
),
],
];
}

Expand All @@ -51,9 +79,12 @@ public function testEnsureThatFloatIsReturned(Closure $assert): void
$assert($this);
}

public function assertEnsureThatFloatIsReturned(?string $value, ?float $expected): void
{
$cast = new FloatCast();
public function assertEnsureThatFloatIsReturned(
?string $value,
?float $expected,
bool $nonNull = false
): void {
$cast = $nonNull === false ? new FloatCast() : new FloatCast(nonNull: $nonNull);
$this->assertSame(
expected: $expected,
actual: $cast->get(model: new Test(), key: '', value: $value, attributes: []),
Expand Down Expand Up @@ -122,6 +153,62 @@ public function dataConvertFloatToModelDecimalValue(): array
cast: new FloatCast(),
),
],
'non null, value' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: 123.23,
expected: '123.23',
cast: new FloatCast(nonNull: true),
),
],
'non null, null' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: null,
expected: '0.00',
cast: new FloatCast(nonNull: true),
),
],
'non null, empty string' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: '',
expected: '0.00',
cast: new FloatCast(nonNull: true),
),
],
'non null, 4 decimals - 1' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: 123.0,
expected: '123.0000',
cast: new FloatCast(decimals: 4, nonNull: true),
),
],
'non null, 4 decimals - 2' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: 123.005,
expected: '123.0050',
cast: new FloatCast(decimals: 4, nonNull: true),
),
],
'non null, 4 decimals - cut' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: 123.00005,
expected: '123.0001',
cast: new FloatCast(decimals: 4, nonNull: true),
),
],
'non null, 2 decimals' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: 123.0,
expected: '123.00',
cast: new FloatCast(decimals: 2, nonNull: true),
),
],
'non null, 1 decimal' => [
static fn (self $self) => $self->assertConvertFloatToModelDecimalValue(
value: 123.0,
expected: '123.0',
cast: new FloatCast(decimals: 1, nonNull: true),
),
],
];
}

Expand Down
20 changes: 20 additions & 0 deletions tests/Feature/Database/Models/Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use LaraStrict\Database\Models\Casts\FloatCast;

/**
* @property int $test
Expand All @@ -19,6 +20,25 @@ class Test extends Model
use SoftDeletes;
final public const AttributeTest = 'test';
final public const AttributeDeletedAt = 'deleted_at';
final public const AttributeFloatNonNull = 'float_non_null';
final public const AttributeFloat = 'float';
final public const AttributeFloat1Decimals = 'float_1_decimals';
final public const AttributeFloat3Decimals = 'float_3_decimals';
final public const AttributeFloat4Decimals = 'float_4_decimals';
final public const AttributeFloat1DecimalsNonNull = 'float_1_decimals_non_null';
final public const AttributeFloat3DecimalsNonNull = 'float_3_decimals_non_null';
final public const AttributeFloat4DecimalsNonNull = 'float_4_decimals_non_null';

protected $casts = [
self::AttributeFloatNonNull => FloatCast::NonNull,
self::AttributeFloat => FloatCast::class,
self::AttributeFloat1Decimals => FloatCast::OneDecimal,
self::AttributeFloat3Decimals => FloatCast::ThreeDecimals,
self::AttributeFloat4Decimals => FloatCast::FourDecimals,
self::AttributeFloat1DecimalsNonNull => FloatCast::OneDecimalNonNull,
self::AttributeFloat3DecimalsNonNull => FloatCast::ThreeDecimalsNonNull,
self::AttributeFloat4DecimalsNonNull => FloatCast::FourDecimalsNonNull,
];

protected $fillable = [self::AttributeTest];
}
151 changes: 151 additions & 0 deletions tests/Unit/Database/Models/Casts/FloatCastFromModelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Unit\Database\Models\Casts;

use Closure;
use PHPUnit\Framework\TestCase;
use Tests\LaraStrict\Feature\Database\Models\Test;

final class FloatCastFromModelTest extends TestCase
{
/**
* @return array<string|int, array{0: Closure(static):void}>
*/
public function data(): array
{
$value = '123.455555555';
return [
'non null, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloatNonNull,
setValue: $value,
expectedValue: 123.46,
),
],
'non null, with null value returns 0.0' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloatNonNull,
setValue: null,
expectedValue: 0.0,
),
],
'nullable default, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat,
setValue: $value,
expectedValue: 123.46,
),
],
'nullable default, with null value returns null' => [
static fn (self $self) => $self->assert(
attribute: 'float_non_null',
setValue: null,
expectedValue: null,
),
],
'1 decimal, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat1Decimals,
setValue: $value,
expectedValue: 123.5,
),
],
'1 decimal, with null value returns null' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat1Decimals,
setValue: null,
expectedValue: null,
),
],
'3 decimals, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat3Decimals,
setValue: $value,
expectedValue: 123.456,
),
],
'3 decimals, with null value returns null' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat3Decimals,
setValue: null,
expectedValue: null,
),
],
'4 decimals, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat4Decimals,
setValue: $value,
expectedValue: 123.4556,
),
],
'4 decimals, with null value returns null' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat4Decimals,
setValue: null,
expectedValue: null,
),
],
'1 decimal non null, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat1DecimalsNonNull,
setValue: $value,
expectedValue: 123.5,
),
],
'1 decimal non null, with null value returns 0.0' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat1DecimalsNonNull,
setValue: null,
expectedValue: 0.0,
),
],
'3 decimals non null, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat3DecimalsNonNull,
setValue: $value,
expectedValue: 123.456,
),
],
'3 decimals non null, with null value returns 0.0' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat3DecimalsNonNull,
setValue: null,
expectedValue: 0.0,
),
],
'4 decimals non null, with value' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat4DecimalsNonNull,
setValue: $value,
expectedValue: 123.4556,
),
],
'4 decimals non null, with null value returns 0.0' => [
static fn (self $self) => $self->assert(
attribute: Test::AttributeFloat4DecimalsNonNull,
setValue: null,
expectedValue: 0.0,
),
],
];
}

/**
* @param Closure(static):void $assert
* @dataProvider data
*/
public function test(Closure $assert): void
{
$assert($this);
}

public function assert(string $attribute, ?string $setValue, ?float $expectedValue): void
{
$test = new Test();
$test->setAttribute($attribute, $setValue);

$this->assertEquals(expected: $expectedValue, actual: $test->getAttribute($attribute));
}
}

0 comments on commit e99a21e

Please sign in to comment.