naxon/nova-field-sortable
naxon/nova-field-sortable is a Laravel Nova field for drag-and-drop sorting of resource records in index views. It lets editors reorder menus, categories, and other lists directly in Nova, with simple setup and an order column to store manual positions. Useful for any model that needs a custom displ...
Architecture fit. The package is a thin Nova field: a PHP Field subclass (src/Sortable.php), a compiled Vue 2 index component, and an AJAX reorder route. It bolts onto the existing Resource::fields() / indexQuery() contract with no new infrastructure, no queue, no service provider of consequence. That is the right shape for the problem — but the scope is strictly one flat list per table. There is no parent/child awareness, no nesting, no per-category scoping. If the requirement is "chapters within a course" or "items within a menu," the field is only the drag handle; the hierarchy, validation, and reindex logic are ours to write.
Integration feasibility. Feasibility is entirely gated on the Nova major version, and that is a one-command check (composer show laravel/nova) before any planning happens.
Nova.booting signature. Budget 1–2 days of frontend work, treat exceeding two days as the signal to write a bespoke field instead — the underlying logic is genuinely shallow.Technical risk. High, and concentrated in ownership rather than complexity.
lockForUpdate() are all ours.public/vendor/, so asset publication becomes a release-pipeline concern, not a one-time install step.Key questions to close before committing.
->sortable() plus $defaultSort already satisfy the requirement? If the order is derivable (name, date, score), this initiative dies here — and that should be the first check, not the last.Stack fit. Laravel + Nova + Eloquent, no frontend build step on our side for Nova 1/2. The one hard dependency friction is that laravel/nova resolves from a private repo — CI needs Nova credentials configured as secrets (not committed auth.json) before this resolves at all.
Migration path.
path repository entry), rather than depending on Packagist. Add the laravel/nova constraint edit as an explicit, reviewed change.unsignedInteger order column with a sane default, backfill from current display order, and set new rows to max(order) + 1 in a booted() creating hook so drags stay meaningful.->onlyOnIndex() — it is a handle, not an editable input — and override indexQuery() to orderBy('order') so the visible list matches stored order.src/Sortable.php for hardcoded order/position first.Compatibility constraints to verify, not assume. Nova major; PHP version against the 2018 constraint; whether a composite unique index (parent_id, order) is needed; and whether any existing sorting mechanism (notably spatie/eloquent-sortable) already owns the order column. Two mechanisms writing the same integer will double-write and drift — pick one.
Sequencing.
Maintenance. This is an adoption of source, not a dependency. Name an owner, pin the commit, and write a short README note describing how our fork diverges — otherwise the next developer assumes it is maintained and attempts an upgrade. Add "verify drag handle renders" to the Nova-major upgrade checklist permanently.
Support. There is none upstream. No issue triage, no security disclosure channel, no peers who will surface breakage before we do. All defects, ports, and vulnerability responses are internal work with no external SLA to point at.
Scaling. The default strategy rewrites N rows per drop, which is correct at tens of rows and wrong past a few hundred. If a list can plausibly grow, plan the fractional-rank design (float or lexicographic) up front so we are not migrating mid-flight; a single-row update per drag also removes most of the lost-update problem for free. Two specific traps: pagination and active filters (a drag on page 2 with a search applied will renumber against visible indices and scramble hidden rows — either disable the handle or send and reindex the full ordered ID set inside the transaction), and multi-parent tables (without the parent in the payload and a composite uniqueness constraint, a drag will renumber across parents).
Failure modes and the debugging playbook.
Nova::script() registration.public/vendor/, so nova:publish must be in the release pipeline or we ship backend changes against last week's frontend.$fillable/mass-assignment or a model saving hook overriding the column.withTrashed() if we ever do.Ramp-up. Low. A few hundred lines across three places — the field class, the Vue component, the route/controller — and a day to be productive. The cost is not learning it; it is owning it indefinitely and remembering that when the next Nova upgrade lands.
How can I help you explore Laravel packages today?