-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[9.x] Add support for casting arrays containing enums #45621
[9.x] Add support for casting arrays containing enums #45621
Conversation
17e6c6b
to
da40aa8
Compare
I'm getting a failure on the MySQL tests on this line due to a space.
Any idea what the preferred way would be to let the MySQL tests pass as well? |
This reverts commit c53c2ff.
Appending a string to something like this feels a bit yucky, but I honestly can't think of a better solution. The only one I've got, and I'm not entirely convinced by it, would be to have: class SomeModel extends Model
{
protected $casts = [
'user_type' => UserType::class,
'user_types' => [UserType::class]
];
} The cast handling code would need to be updated to look for an array of size 1, and if it was, create an array of values of that type. Though, this could possibly break the code out there that expects the cast type to be a |
I'll have a look at this one today to see if we can test both major versions at the same time. |
Seem to have found a solution 👍 |
Thanks @driesvints! Was in doubt whether this issue was already more common or whether such a 'single time patch' solution was better :) Thanks! 🙂 |
One primary concern here is the same concern with the Ideally this would be an object cast like I coded an example implementation that I tested in my own app and it works as expected. Used like this: protected $casts = [
'options' => AsEnumCollection::class.':'.UserOption::class,
]; Code here: <?php
namespace App;
use BackedEnum;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Collection;
class AsEnumCollection implements Castable
{
/**
* Get the caster class to use when casting from / to this cast target.
*
* @param array $arguments
* @return object|string
*/
public static function castUsing(array $arguments)
{
return new class($arguments) implements CastsAttributes {
protected $arguments;
public function __construct(array $arguments)
{
$this->arguments = $arguments;
}
public function get($model, $key, $value, $attributes)
{
if (! isset($attributes[$key]) ||
is_null($attributes[$key])) {
return;
}
$data = json_decode($attributes[$key], true);
if (! is_array($data)) {
return;
}
$enumClass = $this->arguments[0];
return (new Collection($data))->map(function ($value) use ($enumClass) {
return is_subclass_of($enumClass, BackedEnum::class)
? $enumClass::from($value)
: constant($enumClass.'::'.$value);
});
}
public function set($model, $key, $value, $attributes)
{
return [$key => $value->map(function ($enum) {
return $enum instanceof BackedEnum
? $enum->value
: $enum->name;
})->toJson()];
}
public function serialize($model, string $key, $value, array $attributes)
{
return $value->map(function ($enum) {
return $enum instanceof BackedEnum
? $enum->value
: $enum->name;
})->toArray();
}
};
}
} |
@taylorotwell I updated the PR according to what you proposed (with a small change for Do also want to support an |
Added support for |
Today I encountered a situation in an app where I had a database column in JSON that represented an array with enums.
Currently it is not possible to cast the values inside the array to real enums. The array would just contain the backed string or integer values. It would have been quite cumbersome to write a specific cast every time this situation happens.
After some searching I noticed that Spatie's Laravel Enum package does have support for this using the
:array
suffix, which I found nice.This PR implements the casting for enums in an array:
Under the hood, when casting the value, this will store a JSON array with the backed values. When reading the attribute, the json is converted to an array and the backed values are converted to real enums.
Syntax improvements are welcome :)
Thanks!