From 2630d5f00df0b8c4f1d8f1a8f9af4283519c7fd7 Mon Sep 17 00:00:00 2001 From: Matthew Hailwood Date: Sat, 7 Oct 2023 19:31:17 +1300 Subject: [PATCH] Fix toJson method to actually return a json string instead of an array. Add new `AsFullEnumCollection` cast. See the readme for usage and a description of why this is useful. --- README.md | 34 ++++++++++++++ src/Casts/AsFullEnumCollection.php | 71 ++++++++++++++++++++++++++++++ src/IsBackedEnum.php | 16 ++++++- 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/Casts/AsFullEnumCollection.php diff --git a/README.md b/README.md index e13a6f2..262569c 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,40 @@ public function rules(): array } ``` +## Other Classes + +### AsFullEnumCollection +This cast is similar to the Laravel built in `AsEnumCollection` cast but unlike the built in will maintain the full `toArray` structure +when converting to json. + +E.g. the Laravel built in `AsEnumCollection` cast will return the following json: +```json +["MILLIGRAMS", "GRAMS"] +``` +This cast will return +```json +[ + { + "name": "MILLIGRAMS", + "value": "MILLIGRAMS", + "label": "mg", + "meta": { + "background_color": "bg-green-100", + "text_color": "text-green-800" + } + }, + { + "name": "GRAMS", + "value": "GRAMS", + "label": "g", + "meta": { + "background_color": "bg-red-100", + "text_color": "text-red-800" + } + } +] +``` + ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. diff --git a/src/Casts/AsFullEnumCollection.php b/src/Casts/AsFullEnumCollection.php new file mode 100644 index 0000000..ba16562 --- /dev/null +++ b/src/Casts/AsFullEnumCollection.php @@ -0,0 +1,71 @@ + $enumClass + */ + public function __construct(protected string $enumClass) + { + } + + /** + * @return \Illuminate\Support\Collection|null + */ + public function get(Model $model, string $key, mixed $value, array $attributes): ?Collection + { + if (empty($attributes[$key])) { + return new Collection(); + } + + $data = Json::decode($attributes[$key]); + + if (!is_array($data)) { + throw new LogicException('Invalid data for enum collection cast: ' . $attributes[$key]); + } + + + return (new Collection($data))->map(function($value) { + return is_subclass_of($this->enumClass, BackedEnum::class) + ? $this->enumClass::from($value) + : constant($this->enumClass . '::' . $value); + }); + } + + public function set(Model $model, string $key, mixed $value, array $attributes): array + { + $valueArray = Collection::wrap($value) + ->map(fn($enum) => $this->getStorableEnumValue($enum)) + ->jsonSerialize(); + + return [$key => Json::encode($valueArray)]; + } + + protected function getStorableEnumValue($enum) + { + if (is_string($enum) || is_int($enum)) { + return $enum; + } + + return $enum instanceof BackedEnum ? $enum->value : $enum->name; + } +} \ No newline at end of file diff --git a/src/IsBackedEnum.php b/src/IsBackedEnum.php index 2ec7226..5388eb2 100644 --- a/src/IsBackedEnum.php +++ b/src/IsBackedEnum.php @@ -2,7 +2,13 @@ namespace Webfox\LaravelBackedEnums; +use Illuminate\Database\Eloquent\JsonEncodingException; use Illuminate\Validation\Rules\Enum as EnumValidationRule; +use function get_class; +use function json_encode; +use function json_last_error; +use function json_last_error_msg; +use const JSON_ERROR_NONE; /** * @implements \Webfox\LaravelBackedEnums\BackedEnum @@ -100,10 +106,16 @@ public function toHtml(): string return $this->label(); } - public function toJson($options = 0): array + public function toJson($options = 0): string { static::ensureImplementsInterface(); - return $this->toArray(); + $json = json_encode($this->toArray(), $options); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new JsonEncodingException('Error encoding enum ['.get_class($this).'] with value ['.$this->value.'] to JSON: '. json_last_error_msg()); + } + + return $json; } public function is(string|self $value): bool