Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Json Field Laravel Package

ibrahem-kamal/json-field

A Laravel package for working with JSON database fields—cast, store, and validate structured JSON data through an expressive field API, keeping models clean and queries simple.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and confirm it actually matches your Nova version.

composer require ibrahem-kamal/json-field

Before writing a single line of resource code, open vendor/ibrahem-kamal/json-field/composer.json and check:

  • the require block — does it pin laravel/nova to ^4.0, ^5.0, or nothing at all?
  • the autoload.psr-4 namespace — with 1 star and 0 dependents, the namespace is not something you should guess. It will be something like IbrahemKamal\JsonField\ or IbrahemKamal\NovaJsonField\.
  • whether dist/js/field.js is committed. If it is not, the field ships source only and you must npm install && npm run prod inside the package directory (or php artisan nova:publish) before the component renders — otherwise you get a blank white card in the form.

2. Look here first, in this order:

vendor/ibrahem-kamal/json-field/src/            # the Field class, resolve()/fill() logic
vendor/ibrahem-kamal/json-field/resources/js/   # what the editor actually is (CodeMirror? plain textarea?)
vendor/ibrahem-kamal/json-field/README.md       # usually the only docs you get

The two methods that define the entire contract are resolve() (DB → frontend) and fill() (frontend → DB). Read them before trusting anything else, because everything in the Gotchas section below is determined by those two functions.

3. First use case: a meta / settings column.

Add the column if it doesn't exist:

Schema::table('products', function (Blueprint $table) {
    $table->json('meta')->nullable();
});

Cast it on the model — this is the step that decides whether you have a good day or a bad one:

protected $casts = [
    'meta' => 'array',
];

Then in the Nova resource:

use IbrahemKamal\JsonField\JsonField; // verify against psr-4

public function fields(NovaRequest $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Name'),
        JsonField::make('Meta', 'meta')
            ->rules('nullable', 'array')
            ->help('Arbitrary key/value metadata, stored as JSON.'),
    ];
}

Create a record, type {"color":"red","weight":12}, save, then check the raw row with DB::table('products')->first() — not the Nova UI. If the column holds "{\"color\":\"red\"}" (a quoted string), you have the double-encoding bug described below.

Implementation Patterns

Make the editor options explicit. Community JSON fields almost universally expose a meta() bag for editor configuration. Use it rather than editing the compiled JS:

JsonField::make('Payload')
    ->meta([
        'height'     => 400,
        'readOnly'   => false,
        'showErrors' => true,
    ])
    ->resolveUsing(fn ($value) => $value) // only if you need normalisation

Validate twice: the string and the shape.

JsonField::make('Payload', 'payload')
    ->rules('required', 'json')                    // is it parseable at all?
    ->creationRules('json')
    ->updateRules('json')

'json' catches malformed input before it hits the DB. If you also want schema validation, do it in a FormRequest or with a custom rule — Nova's rules() runs against whatever the field submits, which may be a JSON string or a PHP array depending on the field's fill() implementation. Assert which one it is with a quick Log::info(gettype($request->payload)) in store().

Format the index/detail views. Raw {"a":1,"b":2} in a table cell is unreadable and blows up row height. Two options:

JsonField::make('Payload')
    ->hideFromIndex()                                   // simplest
    ->onlyOnDetail();

// or summarise
JsonField::make('Payload')
    ->displayUsing(fn ($value) => collect($value)->keys()->take(3)->implode(', '));

Reuse a configured field across resources. Define it once so editor options and validation don't drift:

// app/Nova/Fields/PayloadJson.php
class PayloadJson extends JsonField
{
    public function __construct($name = 'Payload', $attribute = 'payload')
    {
        parent::__construct($name, $attribute);
        $this->rules('nullable', 'json')->hideFromIndex();
    }
}

Seed and factory data as arrays, not strings. Since the model casts to array, factories should produce ['meta' => ['color' => 'red']]. If you seed with json_encode(...) you'll write a double-encoded string, and the Nova field will then show you a quoted blob on first edit.

Treat the column as opaque in queries. Day-to-day you read the whole document via the model. If you need to filter on a key, either add a generated column in MySQL 5.7+/Postgres (->storedAs("meta->>'color'")) or use whereJsonContains/where('meta->color', 'red') — and then hide the JSON field from index, because it will not be sortable anyway.

Gotchas and Tips

Double encoding is the #1 failure mode. The chain is: Eloquent cast array → Nova serialises to JSON → field fill() runs json_encode() again → you store a JSON string instead of a JSON document. Symptoms: the editor shows escaped quotes on second edit, whereJsonContains stops matching, and Postgres jsonb queries return nothing. Verify with:

dd(DB::table('products')->value('meta')); // expect {"color":"red"}, not "{\"color\":\"red\"}"

Fix by overriding fill() on a subclass, or by removing the model cast and letting the field own serialisation entirely. Pick one owner of the encoding and never both.

Empty string vs null on jsonb. If the field submits '' for an untouched editor, PostgreSQL rejects it (invalid input syntax for type json) and MySQL's JSON_VALID fails. Guard in fill()/resolve():

public function fill(NovaRequest $request, $model)
{
    $value = $this->valueForIndex($request); // or $request->input($this->attribute)
    return parent::fill($request, $model->forceFill([
        $this->attribute => blank($value) ? null : $value,
    ]));
}

->sortable() and ->searchable() silently misbehave on JSON columns. MySQL will order by the document's byte representation; Postgres may error on json (non-jsonb) types. Don't enable them unless you've added a generated column to sort on.

Column size limits bite without warning. MySQL TEXT caps at 64KB and truncates or errors depending on strict mode. If users paste large documents, use MEDIUMTEXT or JSON, and add ->rules('max:60000') as a soft guard.

Nova 4 vs Nova 5 (Vue 2 vs Vue 3). A package with this little activity is unlikely to have been dual-built. If the form renders a blank card, a broken dist/js bundle, or a console error about Vue.component / defineComponent, it's a version mismatch: check package.json for the laravel-nova dev dependency and the Vue major.

If assets look stale, rebuild and re-publish rather than assuming the field is broken:

cd vendor/ibrahem-kamal/json-field && npm install && npm run prod
php artisan nova:publish
php artisan view:clear

Debugging the round-trip. In order: (1) GET /nova-api/{resource}/{id} in the browser network tab — inspect the raw JSON body to see whether the field value is a string or an object; (2) check the PUT/POST payload on save for the same; (3) Log::info() inside resolve() and fill(); (4) storage/logs/laravel.log for validation and DB errors. That sequence localises the fault to DB, Eloquent cast, field class, or Vue component in four steps.

Maintenance risk is the real gotcha. One star, zero dependents, no ecosystem pressure means no security patches and no Nova-5 upgrade path. Treat it as a vendored dependency: fork it into nova-components/json-field inside your own repo (Nova supports local path components), pin the exact version in composer.json, and add a test that asserts resolve()/fill() round-trip a nested array without mutation. If the field is only ever a plain key/value editor, Laravel Nova's built-in KeyValue field or a Textarea with a json rule plus displayUsing(fn ($v) => json_encode($v, JSON_PRETTY_PRINT)) gets you 80% of the value with zero third-party risk — reach for this package only if its syntax-highlighted editor is genuinely worth owning.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console