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

Nova Items Field Laravel Package

blendbyte/nova-items-field

A Laravel Nova field for managing repeatable items as JSON. Define item fields in PHP, let editors add, reorder, and remove rows, and store structured array data on your models—ideal for flexible content blocks and simple repeaters.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

This is a presentation-layer primitive, not a subsystem. It is a single Nova field class (~200 lines) that is a thin withMeta() builder over a Vue form component, with no service provider, no config file, no migrations, and no runtime hooks into the application. That means:

  • Zero architectural footprint. Nothing registers on boot, nothing touches the request lifecycle beyond the resources that explicitly reference the field. Removing it later is a composer remove plus a namespace sweep, not an untangling exercise.
  • It commits you to a data model, not a framework. The real architectural decision is storing an ordered array of {key, value} pairs in one JSON column. That is a legitimate "defer normalization" call for small, uniformly-shaped lists — spec links, opening hours, feature bullets, redirect maps, pricing tiers — and it eliminates the migration + model + relation + policy + admin surface that a HasMany would require.
  • It is a two-column primitive. key and value are hard-coded in the Vue component; keyLabel()/valueLabel() relabel them cosmetically. Anything with three or more attributes per row is out of scope by construction, and forcing it means stuffing JSON into value, which is where this pattern goes bad. That boundary should be written down as a team convention, because it is the single most common misuse.
  • It does not participate in search, filtering, or sorting. The value is one opaque JSON blob to the query layer. If any stakeholder expects "find all records whose link label mentions X," that expectation is not met by this package and requires a denormalized column maintained by an observer.

Integration feasibility

High. The realistic integration is three edits: a json column in a migration, an 'array' cast on the model, and one Items::make() in a resource's fields(). The actual effort — half a day to a day per application — is not in wiring the field but in the adjacent work the package deliberately does not do:

  • Per-row validation lives in the resource's rules() in dot notation (links.*.key, links.*.value), keyed on the attribute name, not the label. This is the classic Nova array-field trap and needs to be a documented convention, not discovered per-resource.
  • A companion read-only Text/Badge field with onlyOnIndex()/onlyOnDetail() is effectively mandatory to keep the index query cheap and to control escaping. Editors see the drag/drop form; the index shows a computed summary.
  • ->default() fires only when the attribute is null on form load. It will not backfill existing rows and will not fire against []. Any migration-time seeding has to be done explicitly.

Technical risk

Risk Severity Mitigation cost
Bus factor / supply chain. 14 stars, 0 dependents, one maintainer, releases driven by outside PRs (1.3.5 is a first-time contributor's one-line fix). Medium–High Low. MIT, tiny surface. Pin the version; vendor into nova-components/ if it stalls.
Tag confusion. The 1.3.5 changelog header reads compare/2.0.1...1.3.5 for structural reasons; 2.0.x tags exist. Floating ^1.0 can cross a boundary nobody has read. Medium Low. Pin ^1.3.5 explicitly and lock. Never infer 1.3.5 is a downgrade.
PHP 8.4 deprecation noise. Older 1.3.x signatures are implicitly nullable and emit a deprecation per request. Medium (if unpinned) Trivial. ^1.3.5 minimum is signature-only, no behavior or storage change.
Concurrent edit clobbering. Whole-array overwrite on save; no per-row IDs; last write wins for the entire list. Medium Process, not code. Decide which content types are safe for multi-admin editing.
Not searchable. searchable() will not reach inside the JSON without DB-specific JSON path wiring. Medium Denormalize via observer, or accept the limitation.
Rendering/XSS. The field stores exactly what the user types; no sanitization. Low e() in every render closure — a lintable convention.
Empty-vs-null ambiguity. The field submits [] and will overwrite a null column; array rules then fail on null. Low Pick one canonical form, normalize in an accessor.

Key questions for the team

  1. Does every candidate row shape have exactly two attributes today, and is there a credible near-term path to three? If three, this is the wrong tool and a HasMany is the cheaper long-term call.
  2. Are we on Nova 5, or is this fork being pulled in as part of a Nova 4 → 5 upgrade? The fork exists because the component contract changed; it is an upgrade enabler, not a drop-in for a Nova 4 shop.
  3. Who owns the vendor contingency if the maintainer stops? A named owner and a pinned lockfile are the whole mitigation.
  4. Which of these lists will ever need to be searched or filtered in the admin, and which need multiple editors on the same record? Those are the disqualifying cases and should be identified before, not after, adoption.
  5. Is the runtime on or moving to PHP 8.4+? If so, do we pin minimum ^1.3.5 in the root composer.json as policy, or trust transitive resolution?

Integration Approach

Stack fit

Exact and narrow: Laravel Nova 5, PHP 8.4+ (with the 1.3.5 minimum), any Laravel version Nova 5 supports. It does not conflict with anything, but it cannot coexist in composer.json with dillingham/nova-items-field — same class name, different vendor segment. That is a hard constraint for teams mid-migration or running mixed applications.

Migration path

Two distinct migrations are in play and should be sequenced separately:

  1. Package swap (from the abandoned original): composer remove dillingham/nova-items-field && composer require blendbyte/nova-items-field, then sweep every resource for Dillingham\NovaItemsField\Items → Blendbyte\NovaItemsField\Items. No data migration — the stored JSON shape is byte-identical. The real work is re-QA on index and detail views, because the field component contract changed on Nova 5.
  2. Nova major upgrade (Nova 4 → 5): the fork is the thing that unblocks it. Treat the namespace sweep and the index/detail re-QA as a named work item in the upgrade plan, not an incidental side effect.

For net-new adoption, there is no migration — just the column, the cast, and the field.

Compatibility

  • Pin ^1.3.5 in the root composer.json and commit the lockfile. Do not float ^1.0 across the 1.3/2.0 tag boundary; read composer show blendbyte/nova-items-field --all before any version change.
  • Prebuilt assets ship in dist/, so no npm install is needed in CI for normal use. Only customizing the Vue source requires a rebuild (npm run prod) and php artisan nova:publish — skipping the publish is the standard "my change did nothing" because Nova serves the cached copy from public/vendor.
  • Verify the namespace against the README before grepping; the fork kept the class name and changed only the vendor.

Sequencing

  1. Pilot on one resource — ideally a low-traffic one with a stable two-attribute shape. Validate the full loop: form entry, drag reorder, validation error placement, index/detail rendering.
  2. Establish conventions as team artifacts, not tribal knowledge:
    • Validation rules in the resource's rules() in dot notation, keyed to the attribute name.
    • Items on forms only; a computed read-only field on index/detail; e() on every interpolation.
    • Canonical empty form ([] vs null) and where it gets normalized.
    • "Two attributes per row" as a written design rule.
  3. Sweep remaining resources, applying the conventions.
  4. Record the vendor contingency — pinned version, named owner, and a decision point (e.g., re-evaluate if no release in N months or if a required PHP bump goes unmerged).

Operational Impact

Maintenance

Low touch, which is both the appeal and the trap. There is no config to drift, no migrations to run, no background process. The maintenance surface is: the pinned version, the namespace references, and the conventions above. The real maintenance risk is that the low activity looks like a solved problem until a PHP runtime bump lands and an implicit-nullable deprecation starts emitting on every request — so the version pin should be owned, not just set. Budget an hour or two per quarter to check tags and confirm the runtime is still supported.

Support

Two distinct audiences:

  • Editors need one piece of guidance that the UI does not provide: nothing in the interface signals drag/drop besides the handle. Use ->help() on every instance to say so, along with any business rule (e.g., "blank URLs are hidden on the site"). Without that, reorder support tickets are guaranteed.
  • Developers need the conventions document. The two failure modes that generate repeat tickets are validation rules keyed to the label instead of the attribute (errors stuck at field level rather than per row) and re-sorting in an accessor (silently discarding the editor's arrangement — persisted array order is the sort order).

Scaling

  • Read cost is fine but not free. The whole JSON blob loads with the model, so an unbounded list inflates every query that touches the record. Cap it with nullable|array|max:20 as a rule, and keep index views on a computed summary rather than rendering the full list.
  • Write concurrency does not scale at all. One column, no per-row identity, whole-array overwrite. Two admins editing different rows of the same record clobber each other, and there is no diff or merge. This is a process constraint: assign single-owner editing for any list that two people might touch, or move genuinely collaborative data to HasMany.
  • Search does not scale past the blob. If link labels ever need to be searchable in Nova, denormalize into a links_text column via an observer — and be honest that at that point a relational model may be the simpler answer.
  • Extension does not scale. Beyond the builder methods, customization means overriding Vue components via Nova::script()/Nova::style() or replacing the field. At ~200 lines, a purpose-built field is frequently cheaper than fighting the layout — so treat any "we need a bespoke layout" request as a signal to re-evaluate rather than extend.

Failure modes (worth putting in a runbook)

Symptom Likely cause First check
Form looks right, model is wrong Double-encoded or empty value getRawOriginal('links') vs $model->links — expect "[]", null, or double-encoded
Per-row errors stuck at field level Rules keyed to label, not attribute Rule keys must be links.*.key, matching Items::make('Links', 'links')
Custom UI change has no effect Assets not published php artisan nova:publish after npm run prod
Deprecation logged every request PHP 8.4 against pre-1.3.5 Version pin in the lockfile
Editor's ordering silently reverted Accessor/cast re-sorting Grep for sortBy in casts and accessors
Last editor's changes vanished Concurrent whole-array overwrite Edit history / who-saved-when

Ramp-up

  • Developer: ~30–60 minutes to first working field, given the README is the API surface. Another hour to internalize the validation-key and empty-value traps.
  • Editor: ~10 minutes, contingent entirely on ->help() explaining the drag handle.
  • On-call / platform team: the failure-mode table above is the whole runbook. The one item that needs pre-agreed policy rather than diagnosis is the vendor contingency — pinned version, named owner, and a willingness to vendor the ~200 lines into nova-components/ if the upstream stalls. Given MIT licensing and the small surface area, that insurance is cheap; the decision to not carry it should be made deliberately, not by default.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager