From 7fde7a1ca93613b2349f7553000f0c88b313d5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89dipo=20Rebou=C3=A7as?= Date: Wed, 18 Oct 2023 15:52:21 -0300 Subject: [PATCH] feat: add backed enum cast --- src/Model/Casts/CastInterface.php | 4 +- src/Model/Casts/CastResolver.php | 11 ++- src/Model/Casts/DateTime/BackedEnumCast.php | 42 +++++++++ .../Casts/Exceptions/InvalidCastException.php | 3 +- .../Casts/Exceptions/InvalidTypeException.php | 18 ++++ tests/Integration/BackedEnumCastTest.php | 70 +++++++++++++++ tests/Stubs/Box.php | 17 ++++ tests/Stubs/Legacy/Box.php | 17 ++++ tests/Stubs/Size.php | 9 ++ tests/Unit/Model/Casts/BackedEnumCastTest.php | 88 +++++++++++++++++++ 10 files changed, 269 insertions(+), 10 deletions(-) create mode 100644 src/Model/Casts/DateTime/BackedEnumCast.php create mode 100644 src/Model/Casts/Exceptions/InvalidTypeException.php create mode 100644 tests/Integration/BackedEnumCastTest.php create mode 100644 tests/Stubs/Box.php create mode 100644 tests/Stubs/Legacy/Box.php create mode 100644 tests/Stubs/Size.php create mode 100644 tests/Unit/Model/Casts/BackedEnumCastTest.php diff --git a/src/Model/Casts/CastInterface.php b/src/Model/Casts/CastInterface.php index df9c4f97..fdaae6a0 100644 --- a/src/Model/Casts/CastInterface.php +++ b/src/Model/Casts/CastInterface.php @@ -6,7 +6,7 @@ interface CastInterface { - public static function get(mixed $value): mixed; + public function get(mixed $value): mixed; - public static function set(mixed $value): mixed; + public function set(mixed $value): mixed; } diff --git a/src/Model/Casts/CastResolver.php b/src/Model/Casts/CastResolver.php index f97ff02c..5ba6bd08 100644 --- a/src/Model/Casts/CastResolver.php +++ b/src/Model/Casts/CastResolver.php @@ -2,6 +2,8 @@ namespace Mongolid\Model\Casts; +use BackedEnum; +use Mongolid\Model\Casts\DateTime\BackedEnumCast; use Mongolid\Model\Casts\DateTime\DateTimeCast; use Mongolid\Model\Casts\DateTime\ImmutableDateTimeCast; use Mongolid\Model\Casts\Exceptions\InvalidCastException; @@ -11,11 +13,6 @@ class CastResolver private const DATE_TIME = 'datetime'; private const IMMUTABLE_DATE_TIME = 'immutable_datetime'; - public static array $validCasts = [ - self::DATE_TIME, - self::IMMUTABLE_DATE_TIME, - ]; - public static function resolve(?string $cast): ?object { if (is_null($cast)) { @@ -25,7 +22,9 @@ public static function resolve(?string $cast): ?object return match($cast) { self::DATE_TIME => new DateTimeCast(), self::IMMUTABLE_DATE_TIME => new ImmutableDateTimeCast(), - default => throw new InvalidCastException($cast), + default => is_a($cast, BackedEnum::class) + ? new BackedEnumCast($cast) + : throw new InvalidCastException($cast) }; } } diff --git a/src/Model/Casts/DateTime/BackedEnumCast.php b/src/Model/Casts/DateTime/BackedEnumCast.php new file mode 100644 index 00000000..990dddc9 --- /dev/null +++ b/src/Model/Casts/DateTime/BackedEnumCast.php @@ -0,0 +1,42 @@ + $backedEnum + */ + public function __construct( + private string $backedEnum + ) { + } + + public function get(mixed $value): mixed + { + if (is_null($value)) { + return null; + } + + return ($this->backedEnum)::from($value); + } + + public function set(mixed $value): mixed + { + if (is_null($value)) { + return null; + } + + if (!$value instanceof $this->backedEnum) { + throw new InvalidTypeException("$this->backedEnum|null", $value); + } + + return $value->value; + } +} diff --git a/src/Model/Casts/Exceptions/InvalidCastException.php b/src/Model/Casts/Exceptions/InvalidCastException.php index 1b9bedae..a1355306 100644 --- a/src/Model/Casts/Exceptions/InvalidCastException.php +++ b/src/Model/Casts/Exceptions/InvalidCastException.php @@ -9,8 +9,7 @@ class InvalidCastException extends InvalidArgumentException { public function __construct(string $cast) { - $available = implode(',', CastResolver::$validCasts); - $message = "Invalid cast attribute: $cast. Use a valid one like $available"; + $message = "Invalid cast attribute: $cast"; parent::__construct($message); } diff --git a/src/Model/Casts/Exceptions/InvalidTypeException.php b/src/Model/Casts/Exceptions/InvalidTypeException.php new file mode 100644 index 00000000..6a8d8543 --- /dev/null +++ b/src/Model/Casts/Exceptions/InvalidTypeException.php @@ -0,0 +1,18 @@ += 8.1 + */ +class BackedEnumCastTest extends IntegrationTestCase +{ + public function testShouldCreateAndSaveBoxWithCastedAttributes(): void + { + // Set + $box = new Box(); + $box->box_size = Size::Big; + + // Actions + $box->save(); + + // Assertions + $this->assertEquals( Size::Big, $box->boxSize); + $this->assertEquals( 'big', $box->getOriginalDocumentAttributes()['box_size']); + } + + public function testShouldUpdateBoxWithCastedAttributes(): void + { + // Set + $box = new Box(); + $box->box_size = Size::Small; + + // Actions + $box->update(); + + // Assertions + $this->assertEquals( Size::Small, $box->boxSize); + $this->assertEquals( 'small', $box->getOriginalDocumentAttributes()['box_size']); + } + + public function testShouldCreateAndSaveLegacyBoxWithCastedAttributes(): void + { + // Set + $legacyBox = new LegacyBox(); + $legacyBox->box_size = Size::Big; + + // Actions + $legacyBox->save(); + + // Assertions + $this->assertEquals( Size::Big, $legacyBox->boxSize); + $this->assertEquals( 'big', $legacyBox->getOriginalDocumentAttributes()['box_size']); + } + + public function testShouldUpdateLegacyBoxWithCastedAttributes(): void + { + // Set + $legacyBox = new LegacyBox(); + $legacyBox->box_size = Size::Small; + + // Actions + $legacyBox->save(); + + // Assertions + $this->assertEquals( Size::Small, $legacyBox->boxSize); + $this->assertEquals( 'small', $legacyBox->getOriginalDocumentAttributes()['box_size']); + } +} + diff --git a/tests/Stubs/Box.php b/tests/Stubs/Box.php new file mode 100644 index 00000000..ac0915b2 --- /dev/null +++ b/tests/Stubs/Box.php @@ -0,0 +1,17 @@ + Size::class, + ]; +} diff --git a/tests/Stubs/Legacy/Box.php b/tests/Stubs/Legacy/Box.php new file mode 100644 index 00000000..b68bd45a --- /dev/null +++ b/tests/Stubs/Legacy/Box.php @@ -0,0 +1,17 @@ + Size::class, + ]; +} diff --git a/tests/Stubs/Size.php b/tests/Stubs/Size.php new file mode 100644 index 00000000..44d624c8 --- /dev/null +++ b/tests/Stubs/Size.php @@ -0,0 +1,9 @@ += 8.1 + */ +class BackedEnumCastTest extends TestCase +{ + public function testShouldGetValue(): void + { + // Set + $cast = new BackedEnumCast(Size::class); + + // Actions + $result = $cast->get('small'); + + // Asserts + $this->assertEquals(Size::Small, $result); + } + + public function testShouldGetNull(): void + { + // Set + $cast = new BackedEnumCast(Size::class); + + // Actions + $result = $cast->get(null); + + // Asserts + $this->assertNull($result); + } + + public function testShoulThrowErrorWhenGetWithUnknownValue(): void + { + // Set + $cast = new BackedEnumCast(Size::class); + + // Expects + $this->expectException(ValueError::class); + + // Actions + $cast->get('unknow value'); + } + + public function testShouldSet(): void + { + // Set + $cast = new BackedEnumCast(Size::class); + + // Actions + $result = $cast->set(Size::Big); + + // Asserts + $this->assertEquals('big', $result); + } + + public function testShouldSetNull(): void + { + // Set + $cast = new BackedEnumCast(Size::class); + + // Actions + $result = $cast->set(null); + + // Asserts + $this->assertNull($result); + } + + public function testShouldThrowErrorWhenSetWithUnknownValue(): void + { + // Set + $cast = new BackedEnumCast(Size::class); + + // Expectations + $this->expectException(InvalidTypeException::class); + $this->expectExceptionMessage('Value expected type ' . Size::class . '|null, given string'); + + // Actions + $cast->set('unknow value'); + } +}