codehero-mx / nova-json-wrapper
Allows you to group Nova fields and merge their output into a single JSON column.
Requires
- php: ^8.2
- laravel/nova: ^5.0
README
A Laravel Nova 5 field that groups multiple Nova fields and stores their values as a single JSON column. Supports nested wrappers, validation, dependsOn for dynamic fields, and works seamlessly on create, update, and detail views.
Requirements
- PHP ^8.2
- Laravel Nova ^5.0
- Vue 3 (included with Nova 5)
Installation
composer require codehero-mx/nova-json-wrapper
The service provider is auto-registered.
Usage
1. Cast the JSON column on your model
class User extends Model { protected $casts = [ 'setting_value' => 'array', ]; }
2. Add HasJsonWrapper trait and define fields in your Nova resource
use CodeheroMx\JsonWrapper\JsonWrapper; use CodeheroMx\JsonWrapper\HasJsonWrapper; use Laravel\Nova\Fields\Number; use Laravel\Nova\Fields\Text; use Laravel\Nova\Http\Requests\NovaRequest; class User extends Resource { use HasJsonWrapper; public function fields(NovaRequest $request): array { return [ Text::make('Name', 'name')->rules('required'), JsonWrapper::make('options', [ Text::make('First Name', 'first_name')->rules('required'), Text::make('Last Name', 'last_name')->rules('required'), Number::make('Age', 'age')->rules('required', 'numeric', 'min:0'), ]), ]; } }
This stores the following JSON in the options column:
{ "first_name": "John", "last_name": "Doe", "age": 30 }
3. Nested wrappers
You can nest JsonWrapper fields to create deep JSON structures:
JsonWrapper::make('options', [ Text::make('First Name', 'first_name')->rules('required'), Text::make('Last Name', 'last_name')->rules('required'), JsonWrapper::make('body_mass', [ Number::make('Weight', 'weight')->rules('required'), Number::make('Height', 'height')->rules('required'), ]), ])
Result:
{
"first_name": "John",
"last_name": "Doe",
"body_mass": {
"weight": 70,
"height": 180
}
}
4. Dynamic fields with dependsOn
JsonWrapper supports Nova's dependsOn to dynamically change the child fields based on another field's value. This is useful when the JSON structure varies depending on a selection.
In the dependsOn callback you receive the JsonWrapper instance — replace its $field->fields collection with the new set of fields and call $field->show() or $field->hide() as needed.
use Laravel\Nova\Fields\FormData; use Laravel\Nova\Fields\Number; use Laravel\Nova\Fields\Select; use Laravel\Nova\Fields\Text; public function fields(NovaRequest $request): array { return [ Select::make('Role', 'role') ->options([ 'developer' => 'Developer', 'designer' => 'Designer', ]) ->rules('required'), JsonWrapper::make('profile', $this->profileFieldsFor($this->resource->role ?? '')) ->dependsOn( ['role'], function (JsonWrapper $field, NovaRequest $request, FormData $formData) { $role = $formData->get('role'); $field->fields = collect(match ($role) { 'developer' => [ Select::make('Language', 'language') ->options(['php' => 'PHP', 'js' => 'JavaScript', 'go' => 'Go']) ->rules('required'), Number::make('Years of Experience', 'experience')->rules('required', 'min:0'), ], 'designer' => [ Text::make('Tool', 'tool')->rules('required'), Text::make('Portfolio URL', 'portfolio_url')->rules('required', 'url'), ], default => [], }); $role ? $field->show() : $field->hide(); }, ), ]; } /** * Return the initial child fields for edit/detail views. */ private function profileFieldsFor(string $role): array { return match ($role) { 'developer' => [ Select::make('Language', 'language') ->options(['php' => 'PHP', 'js' => 'JavaScript', 'go' => 'Go']) ->rules('required'), Number::make('Years of Experience', 'experience')->rules('required', 'min:0'), ], 'designer' => [ Text::make('Tool', 'tool')->rules('required'), Text::make('Portfolio URL', 'portfolio_url')->rules('required', 'url'), ], default => [], }; }
When the user selects a role, the wrapper swaps its child fields accordingly. On edit, existing JSON values are automatically resolved into the fields.
Tip: The initial fields passed to
JsonWrapper::make()are used when the resource already exists (edit/detail). ThedependsOncallback fires on every form sync and replaces them dynamically.
How it works
JsonWrapperextendsLaravel\Nova\Fields\Fieldwith theSupportsDependentFieldstrait. It manages a collection of child fields that are resolved, filled, and validated against the JSON column.HasJsonWrapperis a trait for your Nova Resource that ensures the wrapper is included during form operations (create, update, sync) and flattens child fields on the detail view so they display individually.- On the frontend, the Vue 3 component uses Nova's
DependentFormFieldmixin to handledependsOnsynchronization, visibility toggling, and delegatingfill()to rendered child component instances.
Notes
- There are no visual indications that the fields are wrapped in JSON — they appear as normal Nova fields. This is intentional.
- The
HasJsonWrappertrait is required on any resource that usesJsonWrapper. It handles field visibility across different Nova controllers (create, update, detail, index, sync). - Validation rules on child fields work exactly like regular Nova fields (
->rules('required', 'numeric'), etc.).
Credits
This package is based on nova-json-wrapper by Digital Creative, originally licensed under MIT. It has been upgraded and adapted for Laravel Nova 5.
License
The MIT License (MIT). Please see License File for more information.