Skip to content

Commit

Permalink
Merge pull request #20185 from briedis/master
Browse files Browse the repository at this point in the history
Support BackedEnum attribute typecast behaviour.
  • Loading branch information
mtangoo authored Dec 7, 2024
2 parents dc05f31 + 4b4eb9d commit 49cfd3b
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 11 deletions.
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Yii Framework 2 Change Log
- Enh #20279: Add to the `\yii\web\Request` `csrfHeader` property to configure a custom HTTP header for CSRF validation (olegbaturin)
- Enh #20279: Add to the `\yii\web\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin)
- Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun)
- New #20185: Add `BackedEnum` support to `AttributeTypecastBehavior` (briedis)

2.0.51 July 18, 2024
--------------------
Expand Down
11 changes: 9 additions & 2 deletions framework/behaviors/AttributeTypecastBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
86 changes: 86 additions & 0 deletions tests/framework/behaviors/AttributeTypecastBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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');
}
}
6 changes: 3 additions & 3 deletions tests/framework/db/CommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1538,13 +1538,13 @@ public function testBindValuesSupportsEnums()
$db = $this->getConnection();
$command = $db->createCommand();

$command->setSql('SELECT :p1')->bindValues([':p1' => enums\Status::ACTIVE]);
$command->setSql('SELECT :p1')->bindValues([':p1' => enums\Status::Active]);
$this->assertSame('ACTIVE', $command->params[':p1']);

$command->setSql('SELECT :p1')->bindValues([':p1' => enums\StatusTypeString::ACTIVE]);
$command->setSql('SELECT :p1')->bindValues([':p1' => enums\StatusTypeString::Active]);
$this->assertSame('active', $command->params[':p1']);

$command->setSql('SELECT :p1')->bindValues([':p1' => enums\StatusTypeInt::ACTIVE]);
$command->setSql('SELECT :p1')->bindValues([':p1' => enums\StatusTypeInt::Active]);
$this->assertSame(1, $command->params[':p1']);
} else {
$this->markTestSkipped('Enums are not supported in PHP < 8.1');
Expand Down
4 changes: 2 additions & 2 deletions tests/framework/db/enums/Status.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

enum Status
{
case ACTIVE;
case INACTIVE;
case Active;
case Inactive;
}
4 changes: 2 additions & 2 deletions tests/framework/db/enums/StatusTypeInt.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

enum StatusTypeInt: int
{
case ACTIVE = 1;
case INACTIVE = 0;
case Active = 1;
case Inactive = 0;
}
4 changes: 2 additions & 2 deletions tests/framework/db/enums/StatusTypeString.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

enum StatusTypeString: string
{
case ACTIVE = 'active';
case INACTIVE = 'inactive';
case Active = 'active';
case Inactive = 'inactive';
}

0 comments on commit 49cfd3b

Please sign in to comment.