Cast enum instances nested within JSON or array database columns using Eloquent Attribute accessors and mutators.
Laravel provides first-party enum casting for standard model columns:
protected $casts = [
'status' => PostStatus::class,
];
However, when an enum value is stored inside a nested JSON object or array column (such as options->status or settings['status']), Laravel's default column casting casts the entire column to an array, leaving the nested enum value as a raw string or integer.
Using Eloquent's Attribute accessor and mutator, you can automatically hydrate and serialize nested enum instances.
Implementing the Nested Enum Cast
namespace App\Models;
use App\Enums\PostStatus;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected function options(): Attribute
{
return Attribute::make(
get: function ($value): array {
$options = json_decode($value ?? '[]', true) ?? [];
// Hydrate string value to Backed Enum instance
if (isset($options['status'])) {
$options['status'] = PostStatus::tryFrom($options['status']) ?? $options['status'];
}
return $options;
},
set: function ($value): string {
$options = is_array($value) ? $value : (json_decode($value, true) ?? []);
// Serialize Backed Enum instance to scalar value before storing
if (isset($options['status']) && $options['status'] instanceof PostStatus) {
$options['status'] = $options['status']->value;
}
return json_encode($options);
}
);
}
}
Practical Usage
You can now interact with nested enum properties as strongly typed objects:
$post = Post::find(1);
// Accessing the nested enum
if ($post->options['status'] === PostStatus::Draft) {
// Strongly typed enum comparison
}
// Updating the nested enum
$options = $post->options;
$options['status'] = PostStatus::Published;
$post->options = $options;
$post->save();
Summary
- Use Eloquent
Attribute::make()to hydrate and serialize enums nested inside JSON attributes. - Use
PostStatus::tryFrom()in the getter to gracefully handle unexpected values without throwing exceptions. - Ensures strongly typed enum consistency even for schemaless or flexible JSON payloads.
Related Articles
View all articles →Detect Eloquent Model Attribute Changes Before and After Saving
Use isDirty() and isClean() before saving to inspect unsaved in-memory mutations, and use wasChanged() and getOriginal() after saving to verify persisted changes and access previous values.
Reusable Tappable Scopes for Eloquent and Laravel Scout Queries
Extract query filtering logic into invokable scope classes that work interchangeably across Eloquent query builders and Laravel Scout search instances.