From 1299f70b441c7ce2ff799dc703daf02e603861a3 Mon Sep 17 00:00:00 2001 From: Jeremy Wadhams Date: Mon, 6 May 2024 14:00:02 -0500 Subject: [PATCH] feat: Add a way for Cast classes to express when an attribute should be unset (removed) --- app/Casts/CastFloatOrUnset.php | 21 +++++++ app/Casts/CastNonEmptyStringOrUnset.php | 21 +++++++ app/Casts/CastStringOrUnset.php | 21 +++++++ app/Casts/ShouldUnset.php | 8 +++ app/JsonModel.php | 21 +++++++ tests/Unit/JsonModelTest.php | 76 +++++++++++++++++++++++++ 6 files changed, 168 insertions(+) create mode 100644 app/Casts/CastFloatOrUnset.php create mode 100644 app/Casts/CastNonEmptyStringOrUnset.php create mode 100644 app/Casts/CastStringOrUnset.php create mode 100644 app/Casts/ShouldUnset.php diff --git a/app/Casts/CastFloatOrUnset.php b/app/Casts/CastFloatOrUnset.php new file mode 100644 index 0000000..ab577b8 --- /dev/null +++ b/app/Casts/CastFloatOrUnset.php @@ -0,0 +1,21 @@ +shouldUnset($value)) { + return null; + } + return (float)$value; + } +} \ No newline at end of file diff --git a/app/Casts/CastNonEmptyStringOrUnset.php b/app/Casts/CastNonEmptyStringOrUnset.php new file mode 100644 index 0000000..d9b05db --- /dev/null +++ b/app/Casts/CastNonEmptyStringOrUnset.php @@ -0,0 +1,21 @@ +shouldUnset($value)) { + return null; + } + return (string)$value; + } +} \ No newline at end of file diff --git a/app/Casts/CastStringOrUnset.php b/app/Casts/CastStringOrUnset.php new file mode 100644 index 0000000..6cef148 --- /dev/null +++ b/app/Casts/CastStringOrUnset.php @@ -0,0 +1,21 @@ +shouldUnset($value)) { + return null; + } + return (string)$value; + } +} \ No newline at end of file diff --git a/app/Casts/ShouldUnset.php b/app/Casts/ShouldUnset.php new file mode 100644 index 0000000..7568fe4 --- /dev/null +++ b/app/Casts/ShouldUnset.php @@ -0,0 +1,8 @@ +hasCast($key) ? $this->getCastType($key) : null; + if (is_a($castType, ShouldUnset::class, true) && $castType::shouldUnset($value)) { + unset($this->attributes[$key]); + return $this; + } + + $this->eloquentSetAttribute($key, $value); + return $this; + } } diff --git a/tests/Unit/JsonModelTest.php b/tests/Unit/JsonModelTest.php index be29ad9..ab1c2dd 100644 --- a/tests/Unit/JsonModelTest.php +++ b/tests/Unit/JsonModelTest.php @@ -9,6 +9,9 @@ namespace Tests\Unit; use Carsdotcom\JsonSchemaValidation\Exceptions\JsonSchemaValidationException; +use Carsdotcom\LaravelJsonModel\Casts\CastFloatOrUnset; +use Carsdotcom\LaravelJsonModel\Casts\CastNonEmptyStringOrUnset; +use Carsdotcom\LaravelJsonModel\Casts\CastStringOrUnset; use Carsdotcom\LaravelJsonModel\CollectionOfJsonModels; use Carsdotcom\JsonSchemaValidation\SchemaValidator as SchemaValidator; use Carsdotcom\LaravelJsonModel\JsonModel; @@ -849,6 +852,79 @@ public function testSafeUpdateRecursive(): void self::assertSame('CA', $jsonModel->address->country, 'attribute is invalid, revert to previous value'); self::assertSame('Jeremy', $jsonModel->first_name, 'attribute is valid, set'); } + + public function testCastFloatOrUnset(): void + { + $jsonModel = new class extends JsonModel{ + protected $casts = [ + 'fou' => CastFloatOrUnset::class + ]; + }; + + self::assertFalse(isset($jsonModel->fou), "Initializes unset"); + $jsonModel->fou = null; + self::assertFalse(isset($jsonModel->fou), "Stays unset on initialize to null"); + $jsonModel->fou = 6.9; + self::assertSame(6.9, $jsonModel->fou, "Float is unchanged"); + $jsonModel->fou = '6'; + self::assertSame(6.0, $jsonModel->fou, "String is cast"); + $jsonModel->fou = 9; + self::assertSame(9.0, $jsonModel->fou, "int is cast"); + self::assertStringContainsString('"fou":9', $jsonModel->toJson()); + $jsonModel->fou = null; + self::assertFalse(isset($jsonModel->fou), "Set to null unsets attribute"); + self::assertStringNotContainsString('fou', $jsonModel->toJson()); + } + + public function testCastStringOrUnset(): void + { + $jsonModel = new class extends JsonModel{ + protected $casts = [ + 'sou' => CastStringOrUnset::class + ]; + }; + + self::assertFalse(isset($jsonModel->sou), "Initializes unset"); + $jsonModel->sou = null; + self::assertFalse(isset($jsonModel->sou), "Stays unset on initialize to null"); + $jsonModel->sou = 'happy happy'; + self::assertSame('happy happy', $jsonModel->sou, "String is unchanged"); + $jsonModel->sou = 6; + self::assertSame('6', $jsonModel->sou, "int is cast"); + $jsonModel->sou = false; + self::assertSame('', $jsonModel->sou, "bool is cast"); + self::assertStringContainsString('"sou":""', $jsonModel->toJson()); + $jsonModel->sou = null; + self::assertFalse(isset($jsonModel->sou), "Set to null unsets attribute"); + self::assertStringNotContainsString('sou', $jsonModel->toJson()); + } + + public function testCastNonEmptyStringOrUnset(): void + { + $jsonModel = new class extends JsonModel{ + protected $casts = [ + 'sou' => CastNonEmptyStringOrUnset::class + ]; + }; + + self::assertFalse(isset($jsonModel->sou), "Initializes unset"); + $jsonModel->sou = null; + self::assertFalse(isset($jsonModel->sou), "Stays unset on initialize to null"); + $jsonModel->sou = 'happy happy'; + self::assertSame('happy happy', $jsonModel->sou, "String is unchanged"); + + $jsonModel->sou = ''; + self::assertFalse(isset($jsonModel->sou), "Set to empty string unsets attribute"); + self::assertStringNotContainsString('sou', $jsonModel->toJson()); + + $jsonModel->sou = 6; + self::assertSame('6', $jsonModel->sou, "int is cast"); + self::assertStringContainsString('"sou":"6"', $jsonModel->toJson()); + + $jsonModel->sou = null; + self::assertFalse(isset($jsonModel->sou), "Set to null unsets attribute"); + self::assertStringNotContainsString('sou', $jsonModel->toJson()); + } } /** * These are classes and models needed to test inheritance, etc.