-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[9.x] Add support for casting arrays containing enums (#45621)
* Implement enum array casts * Update typehint, fix merge error * Style * Update HasAttributes.php * Style * Style * Remove unnecessary param * Remove space * Revert "Remove space" This reverts commit c53c2ff. * Try re-arranging commas * wip * wip * wip * Refactor enum array cast to AsEnumCollection * Reset HasAttributes trait * Update AsEnumCollection.php * Style * Update DatabaseEloquentModelTest.php * Fix tests * Style * Fix typo * add support for enum array object Co-authored-by: Dries Vints <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
- Loading branch information
1 parent
7a35c57
commit 0a90dfa
Showing
6 changed files
with
344 additions
and
3 deletions.
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
src/Illuminate/Database/Eloquent/Casts/AsEnumArrayObject.php
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,82 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
use BackedEnum; | ||
use Illuminate\Contracts\Database\Eloquent\Castable; | ||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
use Illuminate\Database\Eloquent\Casts\ArrayObject; | ||
use Illuminate\Support\Collection; | ||
|
||
class AsEnumArrayObject 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 ArrayObject((new Collection($data))->map(function ($value) use ($enumClass) { | ||
return is_subclass_of($enumClass, BackedEnum::class) | ||
? $enumClass::from($value) | ||
: constant($enumClass.'::'.$value); | ||
})->toArray()); | ||
} | ||
|
||
public function set($model, $key, $value, $attributes) | ||
{ | ||
if ($value === null) { | ||
return [$key => null]; | ||
} | ||
|
||
$storable = []; | ||
|
||
foreach ($value as $enum) { | ||
$storable[] = $this->getStorableEnumValue($enum); | ||
} | ||
|
||
return [$key => json_encode($storable)]; | ||
} | ||
|
||
public function serialize($model, string $key, $value, array $attributes) | ||
{ | ||
return (new Collection($value->getArrayCopy()))->map(function ($enum) { | ||
return $this->getStorableEnumValue($enum); | ||
})->toArray(); | ||
} | ||
|
||
protected function getStorableEnumValue($enum) | ||
{ | ||
if (is_string($enum) || is_int($enum)) { | ||
return $enum; | ||
} | ||
|
||
return $enum instanceof BackedEnum ? $enum->value : $enum->name; | ||
} | ||
}; | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php
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,77 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
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) | ||
{ | ||
$value = $value !== null | ||
? (new Collection($value))->map(function ($enum) { | ||
return $this->getStorableEnumValue($enum); | ||
})->toJson() | ||
: null; | ||
|
||
return [$key => $value]; | ||
} | ||
|
||
public function serialize($model, string $key, $value, array $attributes) | ||
{ | ||
return (new Collection($value))->map(function ($enum) { | ||
return $this->getStorableEnumValue($enum); | ||
})->toArray(); | ||
} | ||
|
||
protected function getStorableEnumValue($enum) | ||
{ | ||
if (is_string($enum) || is_int($enum)) { | ||
return $enum; | ||
} | ||
|
||
return $enum instanceof BackedEnum ? $enum->value : $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
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,42 @@ | ||
<?php | ||
|
||
namespace Illuminate\Tests\Database; | ||
|
||
use Illuminate\Contracts\Support\Arrayable; | ||
|
||
enum StringStatus: string | ||
{ | ||
case draft = 'draft'; | ||
case pending = 'pending'; | ||
case done = 'done'; | ||
} | ||
|
||
enum IntegerStatus: int | ||
{ | ||
case draft = 0; | ||
case pending = 1; | ||
case done = 2; | ||
} | ||
|
||
enum ArrayableStatus: string implements Arrayable | ||
{ | ||
case pending = 'pending'; | ||
case done = 'done'; | ||
|
||
public function description(): string | ||
{ | ||
return match ($this) { | ||
self::pending => 'pending status description', | ||
self::done => 'done status description' | ||
}; | ||
} | ||
|
||
public function toArray() | ||
{ | ||
return [ | ||
'name' => $this->name, | ||
'value' => $this->value, | ||
'description' => $this->description(), | ||
]; | ||
} | ||
} |
Oops, something went wrong.