-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
298 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Eloquent Attribute Casting | ||
|
||
Laravel supports casting backed enums out of the box, but what if you don't want to | ||
use backed enums? This is where `CastsBasicEnumerations` comes in. | ||
|
||
## Usage | ||
|
||
```php | ||
enum yourEnum { | ||
case MY_ENUM; | ||
} | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Henzeb\Enumhancer\Laravel\Concerns\CastsBasicEnumerations; | ||
|
||
class YourModel extends Model | ||
{ | ||
use CastsBasicEnumerations; | ||
|
||
$casts = [ | ||
'column' => YourEnum::class | ||
]; | ||
} | ||
|
||
``` | ||
### Lowercase values | ||
By default, it will use of the basic enumeration as the value. If you want the | ||
lowercase variant, you can add the `$keepEnumCase` property and set it to false. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
namespace Henzeb\Enumhancer\Helpers; | ||
|
||
use UnitEnum; | ||
use BackedEnum; | ||
|
||
class EnumValue | ||
{ | ||
public static function value(BackedEnum|UnitEnum $enum, bool $keepCase = false): string|int | ||
{ | ||
return $enum->value ?? ($keepCase ? $enum->name : strtolower($enum->name)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
namespace Henzeb\Enumhancer\Laravel\Concerns; | ||
|
||
use UnitEnum; | ||
use BackedEnum; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Henzeb\Enumhancer\Helpers\EnumValue; | ||
use Henzeb\Enumhancer\Helpers\EnumMakers; | ||
|
||
/** | ||
* @mixin Model | ||
* @property bool $keepEnumCase Change public to private/protected if needed. | ||
*/ | ||
trait CastsBasicEnumerations | ||
{ | ||
protected function getEnumCastableAttributeValue($key, $value) | ||
{ | ||
if (is_null($value)) { | ||
return null; | ||
} | ||
|
||
$castType = $this->getCasts()[$key]; | ||
|
||
/** | ||
* @TODO: remove when fixed: https://github.com/laravel/framework/issues/42658 | ||
*/ | ||
if ($this->shouldUseWorkaround($castType)) { | ||
return $this->enumToArrayWorkaround($value); | ||
} | ||
|
||
if ($value instanceof $castType) { | ||
return $value; | ||
} | ||
|
||
return EnumMakers::make($castType, $value); | ||
} | ||
|
||
protected function setEnumCastableAttribute($key, $value) | ||
{ | ||
$enumClass = $this->getCasts()[$key]; | ||
|
||
$keepEnumCase = property_exists($this, 'keepEnumCase') ? $this->keepEnumCase : true; | ||
|
||
if (!isset($value)) { | ||
$this->attributes[$key] = null; | ||
} elseif ($value instanceof $enumClass) { | ||
$this->attributes[$key] = EnumValue::value($value, $keepEnumCase); | ||
} else { | ||
|
||
if ($value instanceof UnitEnum) { | ||
$value = EnumValue::value($value); | ||
} | ||
|
||
$this->attributes[$key] = EnumValue::value( | ||
EnumMakers::make($enumClass, $value), | ||
$keepEnumCase | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* @TODO: remove when fixed: https://github.com/laravel/framework/issues/42658 | ||
* @param $value | ||
* @return object | ||
*/ | ||
private function enumToArrayWorkaround(string|int $value): object | ||
{ | ||
return new class($value) { | ||
public function __construct(public readonly string|int $value) | ||
{ | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* @TODO: remove when fixed: https://github.com/laravel/framework/issues/42658 | ||
* @param string $enumClass | ||
* @return bool | ||
*/ | ||
private function shouldUseWorkaround(string $enumClass): bool | ||
{ | ||
return (!is_subclass_of($enumClass, BackedEnum::class, true)) | ||
&& 'toArray' === (debug_backtrace(2)[5]['function'] ?? null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
namespace Henzeb\Enumhancer\Tests\Fixtures; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Henzeb\Enumhancer\Laravel\Concerns\CastsBasicEnumerations; | ||
|
||
class CastsBasicEnumsLowerCaseModel extends Model | ||
{ | ||
use CastsBasicEnumerations; | ||
|
||
protected $keepEnumCase = false; | ||
|
||
protected $casts = [ | ||
'unitEnum' => SubsetUnitEnum::class, | ||
'intBackedEnum' => IntBackedEnum::class, | ||
'stringBackedEnum' => StringBackedMakersEnum::class | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace Henzeb\Enumhancer\Tests\Fixtures; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Henzeb\Enumhancer\Laravel\Concerns\CastsBasicEnumerations; | ||
|
||
class CastsBasicEnumsModel extends Model | ||
{ | ||
use CastsBasicEnumerations; | ||
|
||
protected $casts = [ | ||
'unitEnum' => SubsetUnitEnum::class, | ||
'intBackedEnum' => IntBackedEnum::class, | ||
'stringBackedEnum' => StringBackedMakersEnum::class | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
namespace Unit\Laravel\Concerns; | ||
|
||
|
||
use UnitEnum; | ||
use ValueError; | ||
use Orchestra\Testbench\TestCase; | ||
use Henzeb\Enumhancer\Helpers\EnumValue; | ||
use Henzeb\Enumhancer\Tests\Fixtures\IntBackedEnum; | ||
use Henzeb\Enumhancer\Tests\Fixtures\SubsetUnitEnum; | ||
use Henzeb\Enumhancer\Tests\Fixtures\CastsBasicEnumsModel; | ||
use Henzeb\Enumhancer\Tests\Fixtures\StringBackedMakersEnum; | ||
use Henzeb\Enumhancer\Tests\Fixtures\CastsBasicEnumsLowerCaseModel; | ||
|
||
class CastsBasicEnumsTest extends TestCase | ||
{ | ||
public function providesEnums() | ||
{ | ||
return [ | ||
[SubsetUnitEnum::ENUM, 'unitEnum'], | ||
[IntBackedEnum::TEST, 'intBackedEnum'], | ||
[StringBackedMakersEnum::TEST, 'stringBackedEnum'], | ||
|
||
[SubsetUnitEnum::ENUM, 'unitEnum', false], | ||
[IntBackedEnum::TEST, 'intBackedEnum', false], | ||
[StringBackedMakersEnum::TEST, 'stringBackedEnum', false], | ||
]; | ||
} | ||
|
||
/** | ||
* @return void | ||
* | ||
* @dataProvider providesEnums | ||
*/ | ||
public function testShouldCastCorrectlyFromString(UnitEnum $enum, string $key, bool $keepCase = true) | ||
{ | ||
$model = $keepCase ? new CastsBasicEnumsModel() : new CastsBasicEnumsLowerCaseModel(); | ||
$model->setRawAttributes([ | ||
$key => EnumValue::value($enum, $keepCase) | ||
]); | ||
|
||
$this->assertEquals( | ||
$enum, | ||
$model->$key, | ||
); | ||
} | ||
|
||
/** | ||
* @return void | ||
* | ||
* @dataProvider providesEnums | ||
*/ | ||
public function testShouldCastCorrectlyToString(UnitEnum $enum, string $key, bool $keepCase = true) | ||
{ | ||
$model = $keepCase ? new CastsBasicEnumsModel() : new CastsBasicEnumsLowerCaseModel(); | ||
$model->$key = $enum; | ||
|
||
$this->assertEquals( | ||
EnumValue::value($enum, $keepCase), | ||
$model->toArray()[$key], | ||
); | ||
} | ||
|
||
public function testShouldHandleNull() { | ||
$model = new CastsBasicEnumsModel(); | ||
$model->unitEnum = null; | ||
|
||
$this->assertEquals(null, $model->unitEnum); | ||
} | ||
|
||
public function testShouldHandleObjectInAttribute() { | ||
$model = new CastsBasicEnumsModel(); | ||
$model->setRawAttributes(['unitEnum'=>SubsetUnitEnum::ENUM]); | ||
|
||
$this->assertEquals(SubsetUnitEnum::ENUM, $model->unitEnum); | ||
} | ||
|
||
public function testShouldHandleStringValue() { | ||
$model = new CastsBasicEnumsModel(); | ||
$model->unitEnum = 'enum'; | ||
|
||
$this->assertEquals('ENUM', $model->getAttributes()['unitEnum']); | ||
|
||
$this->assertEquals(SubsetUnitEnum::ENUM, $model->unitEnum); | ||
} | ||
|
||
public function testShouldHandleStringValueLowerCase() { | ||
$model = new CastsBasicEnumsLowerCaseModel(); | ||
$model->unitEnum = 'ENUM'; | ||
|
||
$this->assertEquals('enum', $model->getAttributes()['unitEnum']); | ||
} | ||
|
||
public function testShouldFailIfStringIsNotValid() { | ||
$this->expectException(ValueError::class); | ||
$model = new CastsBasicEnumsModel(); | ||
$model->unitEnum = 'NotAnEnum'; | ||
} | ||
|
||
public function testShouldFailIfEnumIsNotValid() { | ||
$this->expectException(ValueError::class); | ||
|
||
$model = new CastsBasicEnumsModel(); | ||
$model->unitEnum = IntBackedEnum::TEST; | ||
} | ||
} |