baril/orderly
Orderly adds orderable/sortable behavior to Laravel Eloquent models. Add a position column and the Orderable trait to reorder records with methods like moveToOffset, using a custom order column if needed.
Feature-level decisions this package touches directly
$groupColumn, including multi-column groups) let us ship "reorder items within a section/category/board" without inventing a bespoke scoping scheme per feature. This unlocks roadmap items like per-section CMS ordering, per-board card ordering, or per-tenant custom ordering — and it's the single most valuable capability in the package for a typical B2B SaaS roadmap.belongsToManyOrderable / morphToManyOrderable support ordering of pivot rows (e.g., tags on a post, contributors on a project, attachments on a record), including ordered attach/sync/detach semantics. This replaces a genuinely error-prone hand-rolled pattern — pivot position columns are where in-house implementations usually break.saveOrder(), setOrder(), and the static/query-builder setOrder() variants support roadmap features like "re-sort by publication date," "import a CSV and apply its order," "reset to default ordering," or "merge two lists." These are the flows that generate the worst support tickets and the most data corruption when written by hand.orderly:fix-positions artisan command (models and pivot relations, with a proper exit code) is a real ops affordance: it makes "positions drifted after a manual DB delete" a scripted remediation instead of a data-migration incident. This is a genuine argument for the package over an in-house trait.Build vs. buy
orderBy('created_at') or a single editorial list that a product manager can reorder weekly. A position column plus orderBy is ~10 lines and the package's surface area isn't earned.Sequencing implication for the roadmap
$guarded entry), this is a good "unblock three features cheaply" decision: it can be folded into whichever of the ordered-list features lands first, rather than meriting its own initiative. Treat it as an enabler inside the first CMS/catalog/board feature, not a standalone workstream.Strong fit — adopt
orderBy.saveOrder, setOrder, collection helpers) for import, re-sort, or merge flows.Proceed with caution — pilot behind an interface
Sortable contract and owning tests for reorder, delete-gap-closing, grouped reorder, and pivot reorder.setOrder. Scope it that way from the start, or the estimate will be wrong.Look elsewhere
For executives / product leadership (one paragraph)
There's a small, free, MIT-licensed Laravel library that provides the "let users reorder things" capability we keep re-specifying: ordered lists, reordering within a category or section, ordered tags and attachments, bulk re-sorts, plus a repair command if the data ever drifts. The realistic alternative is a bespoke trait we build, test, and maintain ourselves — roughly a week of engineering up front plus ongoing bug surface every time a new ordered feature ships. Adopting this reduces that to a schema column and a one-line trait, and it already tracks Laravel versions through 13 and supports MySQL, Postgres, SQLite, and SQL Server. The tradeoff is real but bounded: it's a low-adoption, single-maintainer project with no commercial support, so we should treat it as a dependency we're prepared to own. Recommended posture: adopt it behind a thin internal interface with our own regression tests, which caps the worst case at "we fork a few hundred lines" rather than "we're stuck."
For engineering (the actual ask)
position column and use Orderable on the models that need it; expose reorder actions as an endpoint that calls setOrder([...ids]) on the query builder. Keep the drag-and-drop UI decoupled from the backend primitive.$groupColumn where ordering is per-parent rather than global — this is the right default for most of our list features and the cheapest way to avoid per-feature scoping code.belongsToManyOrdered for ordered pivots instead of adding our own pivot position logic; sync and detach maintain positions for us.saveOrder() / setOrder() over looping moveToOffset(); the README explicitly documents that naive loops cause both N+1 queries and data corruption. Review any PR that loops move methods.fix-positions after manual deletion. These are our insurance against the bus-factor risk, not a reflection of the package's quality.orderly:fix-positions as a documented runbook step after any manual data surgery on ordered tables. Note that the position column must be $guarded.unordered() rather than relying on orderBy to win.Bottom line for the go/no-go conversation: the capability is a clear buy — the cost of building it is real and the cost of adopting it is a migration and a trait. The package is a considered bet on a small, well-maintained, low-visibility project rather than a safe default. Approve adoption with the interface wrapper and our own test coverage; revisit if we hit a scale profile where position-shifting reorders become a bottleneck, or if the maintainer goes quiet across a Laravel major release.
How can I help you explore Laravel packages today?