dillingham/nova-items-field
A Laravel Nova field for managing repeatable items stored in a JSON column. Add, remove, reorder, and search items with a clean, customizable UI. Supports validation, custom templates, and easy integration with Nova resources—ideal for tags, lists, and key-value data.
Architecture fit. The package is a leaf Nova field: one PHP field class in src/ and one Vue 2 component in resources/js/, with no service provider touching the request lifecycle. It maps a single JSON column to a repeatable, drag-sortable editor whose rows are defined by whatever sub-fields you pass to ->fields([...]). That is a narrow, well-bounded contract, which is exactly the profile a TPM wants for a tactical admin-tooling buy: small blast radius, no runtime coupling to queues, auth, or routing, and no schema migration beyond the one JSON column.
It is the right architecture only under a specific set of conditions: the list is edited as a whole, belongs to one parent model, is flat (not nested), and is not a first-class queryable entity. The moment contents need server-side search, filtering, sorting, reporting, or relationships to other models, the correct answer is hasMany plus a Nova relationship field, and this package converts from accelerator into technical debt. That is a scoping rule a TPM can apply in a design review in under five minutes.
Integration feasibility. High, on Nova 3.x only. composer require dillingham/nova-items-field:0.6.*, make the backing column $table->json(...)->nullable(), register the field on a resource. Same-day for a single use case. The critical gate is composer show laravel/nova before anything else: this is a Vue 2 / Laravel Mix-era component, and on Nova 4 (Vue 3) or Nova 5 (Vite/PHP 8.1+) it will almost certainly fail to build. That failure is loud and build-time — a staging failure, not silent production corruption — which makes it tolerable tactically and unacceptable strategically.
Technical risk. Ranked, with the PM-relevant framing:
"1" vs 1, null vs [], orphaned keys). Anything downstream — API, export, analytics — must handle it. The package does not enforce shape; validation lives entirely in the resource's rules() as wildcard per-key rules plus an array-level bound.laravel/nova dependency means builds require Nova repo auth in CI, and security scanning on that private dependency is limited. Every engineer touching the field inherits that constraint.Key questions to answer before committing.
null semantically distinct from [] in this domain — and are we willing to fund downstream branching on two "empty" states? If not, normalize to [] in the resource layer and document it in one place.Stack fit. Requires Laravel Nova 3.x with a JSON (not text) column and model $casts that don't mangle the array. Pin 0.6.* exactly — not ^0.6 — because pre-1.0 semantics mean a minor bump may carry a breaking change, and 0.6 is the tag that carries nullable support. Composes as a leaf field alongside normal Nova fields on the same resource; no dependency on queues, auth, or routing. Nova's standard field API handles readonly() and conditionals — the package adds nothing there, so gate write access with it rather than expecting the package to.
Migration path. If the array is currently edited by hand in a DB client, in a config file, or via ad-hoc scripts, the migration is organizational, not technical: the shape must be documented as a small public contract between the resource layer and any downstream consumer, and validated in exactly one place. If the data currently lives in a relational table and only the editing is being moved into Nova, stop — this is the wrong tool; keep hasMany. There is no data migration burden for the greenfield case beyond adding the column.
Compatibility. Sub-fields define the shape; keep it flat. Nested structures are out of scope and won't render reliably. Built-in drag-and-drop reorder is free and persists as array order in the JSON — no config, but do not rely on that order server-side for anything requiring deterministic sorting; sort explicitly on read. Extension points are minimal by design: if you need min/max item counts, nested repeaters, or per-row conditional logic, verify it exists during the spike rather than assuming — 0.6 adds nullability only.
Sequencing. Run this as a time-boxed spike with a decision gate, not a rollout:
composer show laravel/nova). Hard stop if not 3.x.0.6.*; add the nullable JSON column.null in that column and confirm it loads and saves without coercing to [].rules(); decide and document the null vs [] contract.null before granting edit access.Maintenance. Effectively zero upstream maintenance and that is the point: nobody is obligated to fix this. Support is GitHub issues with uncertain response times, and a Nova license does not extend to third-party fields. The realistic maintenance model is an internally owned fork with a named owner, a pinned version in composer.json, and a line item on the Nova upgrade roadmap that converts the fork cost (3–5 engineer-days) from a surprise into a planned task. Treat the field as an experiment harness with a defined end date.
Support. No vendor SLA. Internal support falls to whoever owns the fork. Debugging is genuinely cheap because the surface is small — read the one PHP field class and the one Vue component, that is the whole package. Practical triage: field renders empty → confirm the column is json not text and $casts aren't mangling the array; save silently no-ops → check validation rules aren't rejecting the array-level key; drag-drop order doesn't persist → confirm the whole array is being saved, since partial writes aren't supported.
Scaling. Two dimensions, and only one of them degrades:
max:N rule as a guardrail; nothing in the package bounds it.hasMany.The real scaling ceiling is organizational: each additional resource using the field multiplies the surface you must re-verify after a Nova upgrade.
Failure modes. Ordered by likelihood and severity:
null isn't meaningful.null/[] split — introduced by adopting 0.6's nullable support. Every consumer now branches on two empty states; decide the semantics once, document them, validate accordingly.Ramp-up. Low for engineers — a competent Laravel dev is productive in hours, because there is one field class and one Vue component to read. The ramp-up that matters is for operators: run the read-only period, then a short enablement session covering add, remove, and drag-reorder, and confirm they understand that saving replaces the whole list. Budget the PM time for the decision, the documented data contract, and the named fork owner; budget the engineering time for the spike and for the upgrade-roadmap line item. Then measure routine engineering tickets on those fields before and after — that number is the business case.
How can I help you explore Laravel packages today?