Download the PHP package exileofaranei/laravel-list-ordering without Composer

On this page you can find all versions of the php package exileofaranei/laravel-list-ordering. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-list-ordering

Laravel List Ordering

Latest Version on Packagist GitHub Tests Action Status Total Downloads

Tight ordering of records within grouped lists using fractional indices: insertion and movement modify a single row, not the entire list.

A list is defined by an arbitrary set of column values on the model — a composite key, a single column, or none at all (one global list). Order is stored as a byte-sortable string rank, not an integer position. Reordering within a list and moving between lists go through the same primitive: placeInto(), expressed entirely in terms of neighbor anchors, never integer positions.

The package knows nothing about the domain it's ordering. It has no concept of trees, hierarchies, or nested sets — it orders flat lists, however those lists are grouped.

Installation

Install via Composer:

The service provider registers itself through Laravel package auto-discovery — no manual registration needed.

The package publishes no migration and no config file. You write your own migration for each model that needs ordering, using the orderingRank() Blueprint macro the package registers:

The unique index always covers the model's group columns plus the rank column — this is what a concurrent write collides against, and what list-ordering:check-index (below) checks your migration against.

Usage

Declaring a model

A model implements Orderable and uses the HasOrdering trait. The trait supplies everything except orderingGroupColumns() — the one method that says which columns define the model's list.

Composite key (both columns not null):

Single column:

Empty list of columns (one global list spanning the whole table):

Placing and moving records

placeInto() is the one primitive. Reordering within a list and moving to a different list are the same call:

Anchors define bounds, not required adjacency — if other rows already sit between the two anchors you pass, the rank is still generated strictly between them. Any anchor you omit is resolved automatically (the next/previous element, or the end of the list if neither is given).

Thin wrappers read by intent, all delegating to placeInto():

Placing a record at an exact rank

placeAtRank() is a second primitive, alongside placeInto(). Where placeInto() computes a rank between two neighbors, placeAtRank() occupies an exact rank value you already have — the case placeInto() deliberately can't cover. It only makes sense for a new record: the saving guard already rejects a rank/group mutation on an already-persisted one.

cloneRankFrom() is the thin wrapper for the usual reason you'd reach for this: copying a record and wanting the copy to sort where the original does within its own list, even though the copy necessarily lives in a different group (a straight copy into the original's own group would collide with the original, which is still occupying that exact rank). It takes the rank from $original but the group from $clone's own group columns — set them (e.g. via mass assignment) before calling it:

A rank collision — two records ending up with the same rank in the same group — surfaces as RankConflictException, same as any other conflict the package's unique index catches.

Reordering a whole list at once

placeInto() and its wrappers move one record relative to its neighbors — that's the only primitive the package has, and batch reordering (recomputing an entire list's order in one shot) is deliberately not a goal of the core API. But it's the first thing you'll need to write against a drag-and-drop UI that hands you a whole list's new order at once (SortableJS and similar), so the package ships it as a separate, opt-in helper: BatchReorderer.

It reads the group's current order itself, diffs it against $newOrderOfKeys, and applies only the moves actually needed — records already in the right relative order are left untouched, so a reorder that moves one item still does one placeAfter() call, not N. The whole operation runs in a single database transaction: either every move lands, or none do.

$newOrderOfKeys must be exactly the group's current keys, just reordered — adding, dropping, or duplicating a key throws InvalidBatchOrderException rather than silently reordering a partial list.

Reading a list in order

Without a GroupKey, ordered() only sorts — it does not, and cannot, guarantee order across different groups in the result. Only omit it when the query is already narrowed to one group some other way (an already-scoped relationship, for example).

A third, optional argument sets sort direction ('asc', the default, or 'desc'):

What the guard does — and doesn't — protect

There are two guards, covering the two ways a write can reach these columns:

A saving guard rejects direct writes to the group or rank columns on an already-persisted model, so a typo like $task->column = 'done'; $task->save(); fails loudly instead of silently corrupting the list. This one only fires on updates to an existing row.

Separately, rank is kept out of mass assignment on every model, new or not — Task::create(['rank' => 'z', ...]) silently drops the rank key rather than writing whatever was passed. This is what lets rank stay off your $fillable list without a workaround: placeInto() and placeAtRank() write it directly (mass-assignment guarding doesn't apply to setAttribute()), so there's never a legitimate reason for anything else to set it. Group columns aren't included in this one — they're routinely set via mass assignment before a placeInto() call (e.g. new Task(['board_id' => 42, ...])), and placeInto() overwrites them from the GroupKey it's given regardless of what was mass-assigned.

This second guard works by adding rank to the model's $guarded array — which Eloquent only ever consults for a key that isn't already in $fillable. If your own model declares protected $fillable = [...] and that list happens to include 'rank' (by copy-paste or oversight), this guard is silently bypassed: $fillable wins before $guarded is checked at all. It protects the common protected $guarded = []; (or no $fillable/$guarded override at all) pattern; it is not a substitute for simply not listing rank in an explicit $fillable.

Neither guard sees insert(), upsert(), withoutEvents(), or any bulk write that bypasses Eloquent's saving/mass-assignment machinery. They're a developer-experience convenience, not the integrity guarantee — that's the database-level composite unique index (group columns + rank), which holds regardless of how a write reaches the table.

Checking your index hasn't drifted

Since migrations are immutable and a model's orderingGroupColumns() can change over time, nothing stops them from silently diverging. Two ways to catch it, both backed by the same check, neither run automatically:

Neither check runs on its own — wire one of them into CI, or the drift goes uncaught until a concurrent write collides in production. The test-based check above runs wherever your existing test suite already runs; to run the artisan command as its own CI step instead:

Testing

Tests run against fixture models from an intentionally unrelated domain via orchestra/testbench — the package's own test suite never references a consuming application. CI runs the full suite against SQLite, MySQL, MariaDB, and PostgreSQL, since collation bugs are invisible on SQLite alone.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.


All versions of laravel-list-ordering with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
spatie/laravel-package-tools Version ^1.16
illuminate/contracts Version ^11.0||^12.0||^13.0
illuminate/database Version ^11.0||^12.0||^13.0
illuminate/support Version ^11.0||^12.0||^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package exileofaranei/laravel-list-ordering contains the following files

Loading the files please wait ...