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

Eloquent Sortable Laravel Package

oddvalue/eloquent-sortable

A Laravel package that adds sortable behavior to Eloquent models. Use a simple trait to assign and maintain custom order, move records up or down, and reorder items reliably—great for drag-and-drop lists, menus, and other ordered content.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What it is. oddvalue/eloquent-sortable is a Laravel trait that maintains a user-defined integer ordering column on Eloquent models — automatic renumbering on create/update/delete, move-before/move-after/move-to-start/move-to-end helpers, an ordered() query scope, and scoped (grouped) ordering so independent lists (per category, per tenant) each keep their own sequence. The description positions it as additional features on top of spatie/eloquent-sortable, so the honest framing for planning purposes is: a small, focused fork of a well-established utility, not a novel platform.

Architecture fit. Integration surface is a trait plus one integer column — no service provider, no config publishing, no custom query builder, no schema rewriting. That is the lowest-coupling shape available and keeps blast radius to the models we explicitly opt in. It solves flat, optionally grouped lists only. It is not a tree/nested-set implementation and ships no drag-and-drop UI. If the roadmap includes hierarchical menus or category trees, this covers a fraction and a second mechanism will be needed.

Release assessment (v2.2.0). The delta from v2.1.0 is four Dependabot PRs touching only GitHub Actions versions (dependabot/fetch-metadata, aglipanci/laravel-pint-action, ramsey/composer-install). No source changes, no public API changes, no deprecations, no schema or config changes, no Composer dependency changes. Functionally this is a CI-maintenance release; upgrade risk from v2.1.x is negligible and the effort is lockfile-only.

Technical risk — this is where a TPM earns their keep:

  • Supply-chain / bus factor (highest concern). 2 GitHub stars, single maintainer, and a fork lineage rather than a canonical upstream. The licence is MIT, which means we can fork or vendor if the maintainer walks away — that is the mitigation, and it should be a written contingency, not an assumption. The strategic question is whether to consume the fork or go directly to spatie/eloquent-sortable upstream; the fork must justify itself with a feature we actually need.
  • Concurrency. A reorder rewrites multiple rows in a group. Two simultaneous drags in the same group can interleave and produce duplicate or gapped positions unless the caller wraps the move in a transaction with row locking. The package gives primitives; the transaction discipline is ours.
  • No database-level guarantee. Ordering uniqueness is enforced in application code. Direct SQL, imports, and seeders will silently corrupt ordering.
  • Cost profile. Moves are O(n) in the affected group size — fine for hundreds to low tens of thousands of rows per group, needs a benchmark before we assume it holds for our largest expected list.
  • Maintenance signal. A dependency-churn-only release reads as maintenance-mode rather than active development. Acceptable for a utility trait; it argues for pinning and for putting ordering behind our own thin service wrapper so we can swap the implementation without touching domain code.

Key questions to resolve before adoption.

  1. Which Laravel and PHP versions does ^2.2 support, and does it cover our target (Laravel 11/12, PHP 8.2+)? Verify from composer.json, not the changelog.
  2. Does it support non-integer primary keys (UUID/ULID) and custom ordering column names?
  3. Which features does this fork add over upstream spatie/eloquent-sortable, and do we actually require any of them? If not, why carry the fork?
  4. Test coverage and CI matrix — what is our confidence level, and what would a fork cost us if we had to maintain it?
  5. Does grouped ordering handle multi-tenant scoping (company_id + order) cleanly, and what happens when a model moves between groups?
  6. Is there a documented migration story from a hand-rolled position column, in case we start there and swap later?

Integration Approach

Stack fit. Drop-in for a standard Laravel app: composer require oddvalue/eloquent-sortable:^2.2, add the trait to the target models, add the ordering column via migration, configure grouping on the model. No bindings, no facades, no config publishing. It coexists with policies, observers, and API resources without interference — which means it will not block or complicate any parallel workstream.

Migration path.

  1. Add a nullable/zero-defaulted integer ordering column to each target table.
  2. Backfill deterministically in one statement per table (ROW_NUMBER() OVER (PARTITION BY <group> ORDER BY created_at, id)), then set NOT NULL.
  3. Add a composite index on (group_column, order_column) — the single highest-value step for both ordered() reads and move performance.
  4. Introduce a thin ReorderService (or per-model action) that wraps every move in DB::transaction() with lockForUpdate() on the affected group, and is the only sanctioned entry point for reordering. This isolates the package and gives us a swap-out seam if the fork is abandoned.
  5. Expose reordering through a dedicated endpoint with authorization and idempotency (accept a target position or neighbour ID, not a full client-side array), then re-read and return the affected slice so the UI stays consistent.
  6. Only then wire the UI (drag-and-drop) to that endpoint.

Compatibility. Pin to ^2.2.0 and commit the lockfile. Because the release contains no source changes, upgrading from 2.1.x is a lockfile-only change — bundle it with routine dependency maintenance rather than scheduling a dedicated window. If starting fresh, adopt 2.2.0 directly.

Sequencing. Schema + backfill + index (one PR, reversible) → service wrapper + tests (one PR) → API endpoint + authorization (one PR) → UI. Do not ship the UI before the transactional endpoint exists, or the first concurrent drag becomes a data-repair ticket. Total engineering shape: small, incremental, no cross-team dependencies — which is exactly the profile that should be scheduled as filler work alongside a larger initiative rather than as a standalone milestone.

Operational Impact

Maintenance. Very low ongoing effort: one Composer dependency, no runtime configuration, no background jobs, no external services, no infra. The recurring task is watching upstream for Laravel major-version support. Given the maintenance-mode signal in this release and the 2-star fork lineage, budget a documented fallback: our wrapper makes a fork or an in-house trait a contained change of roughly a day. Enable Dependabot/Renovate on our side and treat this as a patch-level auto-merge candidate once the wrapper's tests are green.

Support. Community-only: GitHub issues, no SLA, no vendor, effectively one maintainer. Acceptable for a utility trait whose failure mode is contained to ordering and not availability — but it must be recorded in the dependency register as an unsupported component with an owner. We own the semantics we depend on via our wrapper's test suite (moves within group, first/last, cross-group move, empty group, delete-and-renumber).

Scaling. Ordering cost scales with group size, not table size — grouped scoping is what keeps this viable for multi-tenant or per-category lists. The composite index makes ordered() reads cheap. The realistic ceiling is tens of thousands of rows per group per move; beyond that, plan a sparse-ordering variant (large gaps plus a periodic renumber job) or a dedicated position table. Read traffic is unaffected, so browse/list performance scales normally.

Failure modes to monitor.

  • Duplicate or gapped positions from concurrent writes or out-of-band SQL — detect with a periodic integrity check (COUNT(*) vs MAX(order) mismatch per group) and alert.
  • Lock contention or deadlocks on hot groups (e.g. a shared global list) — watch for Deadlock found errors and retry with jitter.
  • Drift after imports or seeders that bypass the wrapper — enforce by convention plus the integrity check; the schema will not stop it.
  • Silent breakage on a Laravel major upgrade — the wrapper's reorder tests are the canary; keep them in CI.
  • Upstream abandonment — the signal is no non-dependency commits over successive releases; check quarterly, and treat two consecutive maintenance-only releases as the trigger to evaluate forking.

Ramp-up. Roughly half a day for a mid-level Laravel engineer to be productive: the API is a handful of methods and one trait. The real learning is not the package but our transactional reorder endpoint and the ordering invariants. Write one page documenting column, index, wrapper, invariants, and integrity check, so the second engineer does not re-derive them. No specialised skills, no infra change, no on-call additions.

Recommendation. Adopt v2.2.0, pinned, behind a thin service wrapper, with the composite index and a transactional reorder endpoint as non-negotiable prerequisites. The release itself is a no-op for our code and should be consumed as routine lockfile maintenance. The decision rests on architectural fit and maintenance-mode tolerance, not on this changelog — and the fork question (build on this vs. go upstream to spatie/eloquent-sortable) should be answered explicitly and recorded before the first PR merges.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect