norman-huth/nova-values-field
norman-huth/nova-values-field adds a flexible values field to Laravel Nova for editing repeatable lists or key-value data in the admin UI, storing structured JSON on Eloquent models. Useful for settings, metadata, tags, and other custom attributes.
This is a single-purpose Nova field: it stores an ordered list of values (a JSON array) instead of a key/value map. It is only worth adopting if you have a Nova resource that needs a free-form repeatable list — tags, aliases, allow-listed emails, feature flags, sizes, etc. — and you don't want the key column Nova's KeyValue forces on you.
Because the repository is archived (last release 2025-06-03, 6 stars, zero dependents), treat it as source material rather than a long-lived dependency:
composer.json and confirm the laravel/nova constraint matches your installed Nova major. Nova fields break silently between majors, so this is the go/no-go check.composer require norman-huth/nova-values-field and verify the field appears.src/ into app/Nova/Fields/Values.php and the compiled assets into public/vendor/, adjusting the namespace. You inherit the maintenance burden either way, so own the code.src/*Field.php (the PHP API surface — methods, jsonSerialize, fill) and resources/js/field.js + components/FormField.vue (what the UI actually submits). The Vue component defines the real data shape; the PHP class is usually a thin wrapper.json/jsonb column (nullable), and cast it on the model:
protected $casts = ['tags' => 'array'];
use NormanHuth\NovaValuesField\Values;
Values::make('Tags', 'tags')
->rules(['nullable', 'array'])
->help('One value per row, order is preserved.');
Basic form + detail wiring. The field is a form input; index and detail views need explicit display logic because an array doesn't render meaningfully by default:
Values::make('Aliases', 'aliases')
->rules(['nullable', 'array', 'max:20'])
->resolveUsing(fn ($value) => is_array($value) ? $value : [])
->displayUsing(fn ($value) => collect($value ?? [])->implode(', '))
->onlyOnForms(); // pair with a Text field for index if needed
Normalize on the model, not the field. Field-level fill behavior is the least reliable part of a small third-party package. Enforce shape with a mutator or cast so imports, seeders, console commands, and API writes stay consistent:
public function setAliasesAttribute($value): void
{
$this->attributes['aliases'] = json_encode(
array_values(array_filter((array) $value, fn ($v) => filled($v)))
);
}
Validation lives outside the field. Rely on standard Laravel rules (array, max:, distinct) rather than any field-provided helpers, since the package is unmaintained. Validate per-item constraints with aliases.* rules:
->rules(['nullable', 'array', 'max:10', 'aliases.*' => ['string', 'max:50']])
Pair with dependsOn for conditional lists. A typical real workflow: a type Select controls whether a values list is shown, then the list feeds downstream logic (e.g., allowed MIME types, environment names).
Use it as a lightweight repeater. For single-column repeaters (ports, CIDR blocks, env var names), this beats installing a full repeater package — but only if your team accepts a vendored asset.
Read/write outside Nova. With the array cast, ordinary code works: $model->aliases[] = 'new'; $model->save();. This is the main reason the field is usable at all despite being archived — it produces plain JSON, not an opaque format.
Integration tip: if you need ordering with drag-and-drop, confirm the Vue component actually emits in display order. If not, sort on save (array_values, sort) rather than fighting the JS.
fill or detail hydration silently drops the array. Test create, update, and detail view before shipping.null vs []. Nova will submit an empty array on save; a fresh record is null. Guard every read with ?? [] and decide deliberately whether empty means null or [] in the database — inconsistent handling causes "why did this record change?" bugs.jsonb ordering. On Postgres, jsonb does not preserve array order semantics the way json does for some operations. If list order is meaningful, store as json (or sort explicitly on read).['a','a'] is valid. Add a distinct rule if that's wrong for your use case.["a","b"] in the index table. Always supply displayUsing, or hide the column on index.npm install && npm run dev inside the package and republish assets — a stale public/vendor/.../field.js is the usual "my changes did nothing" cause.php artisan nova:publish and hard-refresh. Nova caches the manifest aggressively.dd(request()->all()) in the controller, or inspect nova_request in the browser devtools, to confirm the JS emits { value: [...] } and not { value: {0: 'a'} } — PHP will happily cast a keyed object to an array and hide the problem until JSON re-encodes as an object.app/Nova/Fields/ and override jsonSerialize/fill plus copy the Vue component — don't monkey-patch vendor/.assertDatabaseHas with json_encode([...])), not the field object; Nova field classes are hard to unit test meaningfully.How can I help you explore Laravel packages today?