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 Simple Repeatable Laravel Package

optimistdigital/nova-simple-repeatable

A Laravel Nova field package for adding simple repeatable rows to resource forms. It lets editors create, reorder, and remove structured JSON-backed entries without a related model, making flexible repeated inputs easy to manage in Nova.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This is a Nova form field, not a standalone library — it does nothing outside a Nova resource, which sharply bounds its architectural footprint. The integration surface is one nullable json column on an existing table plus a field definition; no child table, no Eloquent relationship, no new model, no shipped migration. The real decision it encodes is storage shape: JSON blob vs. normalized child model. It fits display-bound, human-edited, bounded row sets (opening hours, FAQs, spec tables, contact blocks, feature bullets). It explicitly does not fit anything requiring per-row identity, per-row permissions, audit history, filtering/joining/aggregation, or a public API contract.

Integration feasibility. Three steps: add a nullable JSON column, cast it 'array' on the model (non-optional — without it, saves blow up or read back as null), and declare SimpleRepeatable::make(...)->fields([...]) in the resource. Effort is hours-to-days per resource, and the dominant cost is editor UAT and validation design, not code. Two gating prerequisites: a licensed Nova install (a commercial cost that dwarfs the package), and a Nova major inside the supported matrix. The vendor namespace moved (optimistdigital → outl1ne), so confirm which Packagist name resolves for your Nova version and pin an explicit constraint.

Technical risk. Structurally a small-maintainer package — 75 stars, 0 dependents, effectively unvalidated in the wild. Principal risks: Nova-major upgrade lag (the field will trail Nova releases), no row identity (rows are ordered array entries; removing one renumbers the rest), last-write-wins concurrency with no diff or version, file uploads inside rows leaking storage when rows are deleted, BelongsTo inside rows storing bare IDs with no FK, and likely-unsupported reactive/nested inner fields. The 3.0.1 notes list ->stacked() and an unspecified "updated packages" — treat dependency refreshes as unreviewed until you diff the lockfile.

Key questions to force before committing scope.

  • Will we ever filter, sort, aggregate, or report on an individual row value? (Yes → child model, now.)
  • Will the row shape drift over time, and who owns versioning of that shape?
  • Will this JSON be exposed through an API, export, or downstream integration?
  • What is our Nova upgrade cadence, and who is the named owner for this field's upgrade or fork?
  • Do we need row-level identity (deep links, per-row sync, attachments)?
  • Is the target use case bounded and editor-owned, or unbounded/user-driven?

Integration Approach

Stack fit. Laravel + Nova only. Composer dependency with no meaningful provider configuration and no migrations. Works best where the product lives on admin/ops throughput — content sites, catalogs, internal back-office tools — because it converts a schema-plus-UI workstream into a dependency, a column, and UAT.

Migration path. Forward: one nullable JSON column per use case, cast, field definition. Exit: the JSON stays human-readable, so normalizing later is a decode-and-backfill job — a migration, not a redesign. MIT licensing means the worst case is a fork, not a rewrite. Mitigate exit cost by keeping the resource definition thin — put the inner field set on a static method on the model so a fork or swap is a contained change.

Compatibility. Pin to the exact tag verified against your Nova major; never float the constraint. Verify two things in a branch before designing screens around them: (1) that inner ->rules() validation actually surfaces at the right level in your Nova version — nested validation is the classic weak spot in fields like this; (2) whether dependsOn / conditional visibility keyed off sibling row values works, since reactive and nested-repeatable behavior is the usual breakage point. Treat ->stacked() as cosmetic only — it changes layout, not the stored shape or ordering.

Sequencing.

  1. Confirm Nova major and the resolving Packagist namespace; pin an explicit constraint and commit the lockfile.
  2. Pilot on one low-risk, display-only resource (opening hours or an FAQ list) — bounded, human-edited, never the target of a WHERE or JOIN.
  3. Centralize the row schema as a static field-set method; always pass the second argument to inner make() calls so JSON keys are explicit rather than label-derived.
  4. Add hard model-level validation rules mirroring the field rules — Nova rules are UX, model rules are truth, since seeders, imports, and ->fill() bypass the form.
  5. Hide the field from the index or add a row-count summary; a raw JSON blob in the index is a wall of text.
  6. Add factory fixtures with realistic JSON plus round-trip tests (empty state, add/remove, ordering, max row count).
  7. Only then roll to the next two or three resources. Do not batch-migrate all resources at once, and do not gate the pilot on reporting requirements.

Operational Impact

Maintenance. Low steady-state: no tables to prune, no migrations to run, small surface area. The recurring cost is upgrade ownership — at each Nova major this field will lag, and someone must validate or fork. Cache clearing (view:clear, config:clear, nova:publish) is the first step after any install or upgrade; a blank or unstyled field is almost always compiled assets.

Support. Load shifts from developer tickets to editor UX questions, which is the point. Contain it cheaply by publishing an explicit "do not use this field for X" list at adoption time — that stops the shortcut from spreading into pricing, eligibility, reporting, or integration data where it becomes expensive to unwind. Standardize a first-line debug order: network payload → raw column value → model attribute after fresh() → Vue rendering. That resolves most reports in under a minute and tells you whether it's a field bug or a cast/shape problem.

Scaling. Editorial scale only — tens of records, tens of rows each. The entire document is rewritten on every save, and MySQL JSON is bounded by max_allowed_packet, so thousands of rows is not viable. Also audit whether the column lands in any API response; a large blob there is a real payload cost. If you find yourself writing the third whereJsonContains, that is the signal to migrate to a child model — the documented escape hatch is a generated column extracting and indexing the value.

Failure modes to plan for.

  • Missing array cast → "array to string conversion" on save, or a JSON-encoded string decoding to null on read.
  • null vs [] ambiguity after "delete all rows" → normalize in one accessor rather than branching in fifteen places.
  • Last-write-wins data loss with two editors or the same record in two tabs — no version, no diff, no audit. If that matters, it's a child table.
  • Orphaned files from deleted rows (references removed, files remain) → scheduled prune diffing disk against referenced paths.
  • Dangling BelongsTo IDs inside rows → application-level integrity check in an observer, or avoid relational fields in rows.
  • Silent fixture breakage — an opaque column lets bad shapes pass until an editor opens the record.
  • Attempts at ->searchable() or filters on the column producing nonsense matches and slow scans.

Ramp-up. Low for a Laravel developer — a few hours to a first working field; the learning that matters is shape discipline and validation layering, and it transfers across every subsequent use. Budget separately for editor UAT, which is the real schedule item. Rough sizing: half a day for the pilot field plus tests, plus UAT; subsequent resources materially faster. Because the bet is cheap, reversible, and contained, the correct posture is to adopt it tactically, name an owner, and price the JSON-to-normalized migration into the estimate now rather than discovering it at reporting time.

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.
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator