Replies: 3 comments 8 replies
-
@driesvints, I hope you understand it's a minor change which is a bug anyway as you want to support backed enums and not unit enums. And that I am going to implement the support for unit enums once this bug is fixed, right? |
Beta Was this translation helpful? Give feedback.
-
@henzeb try using a custom cast: <?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes;
class AsEnum implements CastsAttributes, SerializesCastableAttributes
{
protected readonly string $enumName;
public function __construct(string $enumName)
{
if (! \is_subclass_of($enumName, \UnitEnum::class)) {
throw new \InvalidArgumentException($enumName . ' should be an Enum');
}
$this->enumName = $enumName;
}
public function get($model, string $key, $value, array $attributes): ?\UnitEnum
{
if (\is_null($value)) {
return null;
}
if ($value instanceof $this->enumName) {
return $value;
}
if (\is_subclass_of($this->enumName, \BackedEnum::class)) {
return $this->enumName::from($value);
}
foreach ($this->enumName::cases() as $case) {
if ($case->name === $value) {
return $case;
}
}
throw new \InvalidArgumentException('Invalid enum case for: ' . $this->enumName);
}
public function set($model, string $key, $value, array $attributes): array
{
return [$key => $this->serialize($model, $key, $value, $attributes)];
}
public function serialize($model, string $key, $value, array $attributes)
{
if (\is_null($value)) {
return null;
}
['value' => $enumValue] = ((array) $value) + ['value' => $value->name];
return $enumValue;
}
} Usage: <?php // ./routes/console.php
use App\Casts\AsEnum;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Artisan;
enum Foo
{
case BAR;
case BAZ;
}
enum Qux: int
{
case BAR = 1;
case BAZ = 2;
}
class Dummy extends Model
{
protected $attributes = [
'unit' => Foo::BAR,
'backed' => Qux::BAR,
'backed_value' => '1',
];
protected $casts = [
'unit' => AsEnum::class . ':' . Foo::class,
'backed' => AsEnum::class . ':' . Qux::class,
'backed_value' => AsEnum::class . ':' . Qux::class,
];
}
Artisan::command('test', function () {
$model = new Dummy();
\dump(
$model->unit,
$model->unit instanceof Foo,
$model->backed,
$model->backed instanceof Qux,
$model->backed_value,
$model->backed_value instanceof Qux,
$model->toArray(),
);
$model->unit = Foo::BAZ;
$model->backed = Qux::BAZ;
$model->backed_value = Qux::BAZ;
\dump(
$model->unit,
$model->unit instanceof Foo,
$model->backed,
$model->backed instanceof Qux,
$model->backed_value,
$model->backed_value instanceof Qux,
$model->toArray(),
);
}); Output: $ php artisan test
^ Foo^ {#532
+name: "BAR"
}
^ true
^ Qux^ {#544
+name: "BAR"
+value: 1
}
^ true
^ Qux^ {#544
+name: "BAR"
+value: 1
}
^ true
^ array:3 [
"unit" => "BAR"
"backed" => 1
"backed_value" => 1
]
^ Foo^ {#880
+name: "BAZ"
}
^ true
^ Qux^ {#876
+name: "BAZ"
+value: 2
}
^ true
^ Qux^ {#876
+name: "BAZ"
+value: 2
}
^ true
^ array:3 [
"unit" => "BAZ"
"backed" => 2
"backed_value" => 2
] references:
If you believe a custom cast like this belongs to the core, try to PR it |
Beta Was this translation helpful? Give feedback.
-
That also works, but I don't see the point. It's not a fast solution. It's a very cloggy solution to have a class FQCN concatenated in a string with another. What belongs to the core of PHP is a basic enumeration object with a value that returns the name or a lowercase version of this name when asking for a value.What belongs to the core of Laravel is the acceptance of basic enumerations and use their names. a PR would most likely not make it unless the team behind laravel agrees with it. As such a change would also require to accept such enums in validation or routing (consistency), I doubt it. The bug in #42658 is, as far as I can see, just a problem in the toArray method. If they won't fix it, I already have an idea for a workaround. |
Beta Was this translation helpful? Give feedback.
-
Description:
I am working on my package henzeb/enumhancer And I want to make UnitEnums castable as I find it ridiculous that we have to write 'CASE ENUM='ENUM' . It is redundant.
I could of course make an adapter class for every enum I create, but I want to make it easy for people. Just add a trait to your UnitEnum and you're done.
The problem is that currently Laravel is checking if it is an enum at all. So if it is an enum, no matter if it is a BackedEnum or a UnitEnum, it will use the enum casting functionality you build. Ofcourse, with UnitEnums, it crashes.
Steps To Reproduce:
1 . create a UnitEnum (without int or string)
2. create a table and a Model using a field that is using those enum cases
3. add the enum class path to the casts property
4. run it.
All I am asking is that you change the way it verifies if it is a BackedEnum using is_subclass_of. It won't change anything on how you implement the casting, just the error message people receive when trying an UnitEnum
I'll take care of the rest.
Beta Was this translation helpful? Give feedback.
All reactions