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'); + } +}