From a4ecd76a539f46ef502a9088ff68dc630e8b6947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C4=81rti=C5=86=C5=A1=20Briedis?= <mbriedis@printful.com> Date: Tue, 4 Jun 2024 15:52:32 +0300 Subject: [PATCH] Support BackedEnum attribute typecast behaviour. --- .../behaviors/AttributeTypecastBehavior.php | 11 ++- .../AttributeTypecastBehaviorTest.php | 86 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/framework/behaviors/AttributeTypecastBehavior.php b/framework/behaviors/AttributeTypecastBehavior.php index b313cac1b61..d3a177b3d9e 100644 --- a/framework/behaviors/AttributeTypecastBehavior.php +++ b/framework/behaviors/AttributeTypecastBehavior.php @@ -267,9 +267,16 @@ protected function typecastValue($value, $type) return StringHelper::floatToString($value); } return (string) $value; - default: - throw new InvalidArgumentException("Unsupported type '{$type}'"); } + + if (PHP_VERSION_ID >= 80100 && is_subclass_of($type, \BackedEnum::class)) { + if ($value instanceof $type) { + return $value; + } + return $type::from($value); + } + + throw new InvalidArgumentException("Unsupported type '{$type}'"); } return call_user_func($type, $value); diff --git a/tests/framework/behaviors/AttributeTypecastBehaviorTest.php b/tests/framework/behaviors/AttributeTypecastBehaviorTest.php index 5af290672e1..124dd483f74 100644 --- a/tests/framework/behaviors/AttributeTypecastBehaviorTest.php +++ b/tests/framework/behaviors/AttributeTypecastBehaviorTest.php @@ -7,11 +7,13 @@ namespace yiiunit\framework\behaviors; +use ValueError; use Yii; use yii\base\DynamicModel; use yii\base\Event; use yii\behaviors\AttributeTypecastBehavior; use yii\db\ActiveRecord; +use yiiunit\framework\db\enums\StatusTypeString; use yiiunit\TestCase; /** @@ -47,6 +49,7 @@ protected function setUp(): void 'price' => 'float', 'isActive' => 'boolean', 'callback' => 'string', + 'status' => 'string', ]; Yii::$app->getDb()->createCommand()->createTable('test_attribute_typecast', $columns)->execute(); } @@ -80,6 +83,55 @@ public function testTypecast() $this->assertSame('callback: foo', $model->callback); } + public function testTypecastEnum() + { + if (PHP_VERSION_ID < 80100) { + $this->markTestSkipped('Can not be tested on PHP < 8.1'); + } + + $model = new ActiveRecordAttributeTypecastWithEnum(); + + $model->status = StatusTypeString::ACTIVE; + + $model->getAttributeTypecastBehavior()->typecastAttributes(); + + $this->assertSame(StatusTypeString::ACTIVE, $model->status); + } + + /** + * @depends testTypecastEnum + */ + public function testTypecastEnumFromString() + { + if (PHP_VERSION_ID < 80100) { + $this->markTestSkipped('Can not be tested on PHP < 8.1'); + } + + $model = new ActiveRecordAttributeTypecastWithEnum(); + $model->status = 'active'; // Same as StatusTypeString::ACTIVE->value; + + $model->getAttributeTypecastBehavior()->typecastAttributes(); + + $this->assertSame(StatusTypeString::ACTIVE, $model->status); + } + + /** + * @depends testTypecastEnum + */ + public function testTypecastEnumFailWithInvalidValue() + { + if (PHP_VERSION_ID < 80100) { + $this->markTestSkipped('Can not be tested on PHP < 8.1'); + } + + $model = new ActiveRecordAttributeTypecastWithEnum(); + $model->status = 'invalid'; + + self::expectException(ValueError::class); + + $model->getAttributeTypecastBehavior()->typecastAttributes(); + } + /** * @depends testTypecast */ @@ -339,3 +391,37 @@ public function getAttributeTypecastBehavior() return $this->getBehavior('attributeTypecast'); } } + +/** + * Test Active Record class with [[AttributeTypecastBehavior]] behavior attached with an enum field. + * + * @property StatusTypeString $status + */ +class ActiveRecordAttributeTypecastWithEnum extends ActiveRecord +{ + public function behaviors() + { + return [ + 'attributeTypecast' => [ + 'class' => AttributeTypecastBehavior::className(), + 'attributeTypes' => [ + 'status' => StatusTypeString::class, + ], + 'typecastBeforeSave' => true, + ], + ]; + } + + public static function tableName() + { + return 'test_attribute_typecast'; + } + + /** + * @return AttributeTypecastBehavior + */ + public function getAttributeTypecastBehavior() + { + return $this->getBehavior('attributeTypecast'); + } +}