### Install LexoRank Sortable with Composer
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Install the package using Composer. Ensure your Laravel version is compatible with the sortable version.
```bash
composer require alexcrawford/lexorank-sortable
```
--------------------------------
### Usage Examples for SortableTrait
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Demonstrates auto-positioning on create, retrieving sorted items, moving items before or after others, and navigating siblings. Also shows how to handle deletion gaps and calculate new positions.
```php
// 1. Auto-position on create — no need to set position manually
$a = Article::create(['title' => 'First']); // position => 'U'
$b = Article::create(['title' => 'Second']); // position => 'V'
$c = Article::create(['title' => 'Third']); // position => 'X'
// 2. Retrieve in sorted order
$articles = Article::sorted()->get();
// => First, Second, Third
// 3. Move $a after $c
$a->moveAfter($c);
// $a->position is now lexicographically after $c->position
// Order is now: Second, Third, First
// 4. Move $b before $a (which is now last)
$b->moveBefore($a);
// Order is now: Third, Second, First
// 5. Moving an entity to itself is a no-op (safe)
$c->moveAfter($c); // nothing changes
// 6. Navigate siblings
$entity = Article::find(5);
$prev = $entity->getPrevious(); // Collection of all items before this one
$next = $entity->getNext(3); // Collection of up to 3 items after this one
// 7. Handle deletion gaps (optional, in a ServiceProvider)
Article::deleting(function (Article $model) {
// shift subsequent items up if gap-free ordering is required
$model->next()->decrement('sort_order');
});
// 8. Static helper — calculate a raw position string
$newPos = Article::getNewPosition('U', 'V'); // returns a rank between 'U' and 'V'
```
--------------------------------
### Sortable Groups Usage Example
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Demonstrates creating cards, reordering them within the same group, and handling exceptions when attempting to move cards between groups.
```php
// --- Usage ---
$todo = BoardColumn::find(1);
$inProgress = BoardColumn::find(2);
// Cards are auto-positioned within their column
$card1 = Card::create(['title' => 'Task A', 'board_column_id' => $todo->id]);
$card2 = Card::create(['title' => 'Task B', 'board_column_id' => $todo->id]);
$card3 = Card::create(['title' => 'Task C', 'board_column_id' => $todo->id]);
// Reorder within the same column — works fine
$card1->moveAfter($card3);
// Try to move across groups — throws SortableException
try {
$crossColumnCard = Card::create(['title' => 'Task D', 'board_column_id' => $inProgress->id]);
$card1->moveAfter($crossColumnCard); // throws!
} catch (SortableException $e) {
echo $e->getMessage();
// "You can't move entities with different sortable group: 1 2"
}
// Retrieve all cards for a specific column in order
$columnCards = Card::where('board_column_id', $todo->id)->sorted()->get();
```
--------------------------------
### BelongsToSortedManyTrait Usage Example
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Demonstrates attaching, saving, syncing, and reordering tags for a post. Related items are always retrieved in their sorted order.
```php
// --- Usage ---
$post = Post::create(['title' => 'Hello World']);
$tag1 = Tag::create(['name' => 'PHP']);
$tag2 = Tag::create(['name' => 'Laravel']);
$tag3 = Tag::create(['name' => 'Eloquent']);
// Attach — position assigned automatically
$post->tags()->attach($tag1->id); // position 'U'
$post->tags()->attach($tag2->id); // position 'V'
$post->tags()->attach($tag3->id); // position 'X'
// Or save via relation (also auto-positions)
$post->tags()->save($tag1);
// Sync — detaches old, attaches new in given order with fresh positions
$post->tags()->sync([$tag3->id, $tag1->id, $tag2->id]);
// Read — always returned in position order
foreach ($post->tags as $tag) {
echo $tag->name . ' => ' . $tag->pivot->position . PHP_EOL;
}
// Eloquent: position 'U'
// PHP: position 'V'
// Laravel: position 'X'
// Reorder: move tag3 before tag1
$tag3loaded = $post->tags()->find($tag3->id);
$tag1loaded = $post->tags()->find($tag1->id);
$post->tags()->moveBefore($tag3loaded, $tag1loaded);
// tag3 is now first in the list for this post
// Reorder: move tag2 after tag3
$tag2loaded = $post->tags()->find($tag2->id);
$post->tags()->moveAfter($tag2loaded, $tag3loaded);
```
--------------------------------
### Navigate Sibling Entities with Previous and Next
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Utilizes `previous()` and `next()` methods to get query builders for sibling entities relative to the current model's position. `getPrevious()` and `getNext()` return hydrated collections.
```php
$article = Article::find(5);
// Query builder — chainable
$prevQuery = $article->previous(); // all items before, closest first
$nextQuery = $article->next(); // all items after, closest first
$prevLimited = $article->previous(3); // limit to 3
$nextLimited = $article->next(2); // limit to 2
// Hydrated collections
$prevItems = $article->getPrevious(); // Collection, ordered ascending (natural list order)
$nextItems = $article->getNext(); // Collection
$prev3 = $article->getPrevious(3); // last 3 items before this one, in list order
$next2 = $article->getNext(2); // next 2 items after this one
// Practical: get the immediately adjacent items
$justBefore = $article->previous(1)->first();
$justAfter = $article->next(1)->first();
echo "Before: " . ($justBefore?->title ?? 'none');
echo "After: " . ($justAfter?->title ?? 'none');
// Use siblings() directly for custom direction logic
$after = $article->siblings(true, 5)->get(); // 5 items after
$before = $article->siblings(false, 5)->get(); // 5 items before
```
--------------------------------
### AJAX Payload for Simple Entity Sorting
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
This is an example of the AJAX POST payload expected by the SortableController for simple entity sorting. It includes the type of move, the entity name, the ID of the entity to move, and the ID of the anchor entity.
```json
{
"type": "moveAfter", // "moveAfter" | "moveBefore"
"entityName": "articles", // key from config/sortable.php
"id": 3, // entity to move
"positionEntityId": 14 // anchor entity
}
```
--------------------------------
### Get Sorted Tags for a Post
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Retrieves the tags associated with a post, automatically sorted by their position.
```php
$post->tags; // ordered by position by default
```
--------------------------------
### Get Ordered Entities with Sorted Scope
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Use the `sorted` scope on your model to retrieve entities in their correct sorted order.
```php
$articles = Article::sorted()->get();
```
--------------------------------
### AJAX Payload for Many-to-Many Entity Sorting
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
This example shows the AJAX POST payload for sorting entities in a many-to-many relationship. It includes the parent ID, the entity ID to move, and the anchor entity ID within the pivot table context.
```json
{
"type": "moveAfter",
"entityName": "posts",
"parentId": 7, // the Post id
"id": 3, // Tag id to move
"positionEntityId": 14 // anchor Tag id
}
```
--------------------------------
### Calculate New Position Between Ranks
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Use this static helper to calculate a LexoRank string for a slot between two existing positions. It's useful for custom ordering logic outside of Eloquent events. Handles cases for inserting between items, at the end, at the start, or in an empty list.
```php
use AlexCrawford\Sortable\SortableTrait;
// Position between two existing ranks
$between = Article::getNewPosition('U', 'V');
// => e.g., 'UV' (a string that sorts between 'U' and 'V')
// Position after the last item (no upper bound)
$atEnd = Article::getNewPosition('Z');
// => rank after 'Z'
// Position before the first item (no lower bound)
$atStart = Article::getNewPosition(null, 'U');
// => rank before 'U'
// Position in an empty list
$first = Article::getNewPosition(null, null);
// => initial rank, e.g., 'U'
```
```php
// Manual drag-and-drop handler (without using the built-in controller)
$dragged = Article::find($draggedId);
$prev = Article::find($prevId); // item now above the drop target
$next = Article::find($nextId); // item now below the drop target
$dragged->position = Article::getNewPosition($prev->position, $next->position);
$dragged->save();
```
--------------------------------
### Publish Configuration
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Run this Artisan command to publish the sortable configuration file to your project.
```bash
php artisan vendor:publish
```
--------------------------------
### Create Posts and Tags Tables Migrations
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Sets up the 'posts', 'tags', and 'post_tag' tables. The pivot table includes a 'position' column with appropriate collation for sorting.
```php
// --- Migrations ---
Schema::create('posts', fn (Blueprint $t) => [$t->id(), $t->string('title')]);
Schema::create('tags', fn (Blueprint $t) => [$t->id(), $t->string('name')]);
Schema::create('post_tag', function (Blueprint $table) {
$table->unsignedBigInteger('post_id');
$table->unsignedBigInteger('tag_id');
// MySQL/MariaDB — binary collation required:
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin');
// SQLite/PostgreSQL: $table->string('position');
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->primary(['post_id', 'tag_id']);
});
```
--------------------------------
### Docker Build Command
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Builds the Docker image for the project. Tag the image as 'alexcrawford-sortable'.
```bash
docker build -t alexcrawford-sortable .
```
--------------------------------
### Laravel Pint for Code Formatting
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Commands to check and fix code style using Laravel Pint. Use '--test' to only check without modifying files.
```bash
vendor/bin/pint # Check and fix code style
vendor/bin/pint --test # Check only (don't modify)
```
--------------------------------
### Docker Run Command for All Tests
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Executes PHPUnit, PHPStan, and Laravel Pint checks sequentially within a Docker container to ensure code quality and test coverage.
```bash
docker run --volume $PWD:/project --rm --user $(id -u):$(id -g) alexcrawford-sortable bash -c "vendor/bin/phpunit && vendor/bin/phpstan analyse && vendor/bin/pint --test"
```
--------------------------------
### Publish Sortable Configuration
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Publish the sortable configuration file using the Artisan command to customize entity sorting configurations.
```bash
php artisan vendor:publish
// Creates: config/sortable.php
```
--------------------------------
### Docker Run Command for Code Coverage
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Runs PHPUnit tests with code coverage enabled, outputting results in text format within a Docker container.
```bash
docker run --volume $PWD:/project --rm --user $(id -u):$(id -g) alexcrawford-sortable vendor/bin/phpunit --coverage-text
```
--------------------------------
### Register Service Provider
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Add the SortableServiceProvider to your application's configuration file to enable the package's functionality.
```php
'providers' => array(
// providers...
'AlexCrawford\Sortable\SortableServiceProvider',
)
```
--------------------------------
### Docker Run Command for Tests
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Runs PHPUnit tests within a Docker container. It mounts the current directory to '/project' and runs tests using the specified user.
```bash
docker run --volume $PWD:/project --rm --user $(id -u):$(id -g) alexcrawford-sortable vendor/bin/phpunit
```
--------------------------------
### Attach and Reorder Tags in Polymorphic Relationship
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Demonstrates attaching tags to polymorphic relations, where positions are set automatically. It also shows how to reorder tags for a specific parent model.
```php
$post = Post::create(['title' => 'Hello']);
$video = Video::create(['title' => 'Demo']);
$tag1 = Tag::create(['name' => 'News']);
$tag2 = Tag::create(['name' => 'Featured']);
$tag3 = Tag::create(['name' => 'Popular']);
// Attach to polymorphic relation — position set automatically per parent
$post->tags()->attach($tag1->id);
$post->tags()->attach($tag2->id);
$post->tags()->attach($tag3->id);
$video->tags()->attach($tag2->id); // independent position sequence
// Retrieve sorted
foreach ($post->tags as $tag) {
echo $tag->name . ' => ' . $tag->pivot->position . PHP_EOL;
}
// Reorder
$t1 = $post->tags()->find($tag1->id);
$t3 = $post->tags()->find($tag3->id);
$post->tags()->moveAfter($t1, $t3); // tag1 is now after tag3 for this post only
```
--------------------------------
### Configure Sortable Entities
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Define the entities and their corresponding models that you want to make sortable in the `config/sortable.php` file. Supports direct entity mapping or specifying a relation for many-to-many relationships.
```php
'entities' => array(
'articles' => '\App\Article', // entityNameForUseInRequest => ModelName
// or
'articles' => ['entity' => '\App\Article'],
// or for many to many
'posts' => [
'entity' => '\App\Post',
'relation' => 'tags' // relation name (method name which returns $this->belongsToSortedMany)
]
),
```
--------------------------------
### Attach Tags to Post
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Demonstrates methods for attaching tags to a post, which automatically sets their position. Supports `save`, `attach`, and `sync` operations.
```php
$post->tags()->save($tag) // or
$post->tags()->attach($tag->id) // or
$post->tags()->sync([$tagId1, $tagId2, /* ...tagIds */])
```
--------------------------------
### Configure Sortable Entities
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Define the entities and their associated models or relations in the config/sortable.php file to enable sorting for different parts of your application.
```php
return [
'entities' => [
// Simple model sorting
'articles' => \App\Models\Article::class,
// Or explicit array form
'cards' => ['entity' => \App\Models\Card::class],
// Many-to-many pivot sorting
'posts' => [
'entity' => \App\Models\Post::class,
'relation' => 'tags', // method name returning belongsToSortedMany
],
],
];
```
--------------------------------
### Define Sortable Groups with Single Field
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Configure a model to partition sortable items based on a single foreign key. Ensure the foreign key column is indexed for performance.
```php
use Illuminate\Database\Eloquent\Model;
use AlexCrawford\Sortable\SortableTrait;
use AlexCrawford\Sortable\SortableException;
class Card extends Model
{
use SortableTrait;
// Single-field group
protected static $sortableGroupField = 'board_column_id';
// --- or multi-field group ---
// protected static $sortableGroupField = ['board_id', 'board_column_id'];
}
```
--------------------------------
### Define Sort Route
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Add this route definition to your `routes/web.php` or `routes/api.php` file to handle sorting requests.
```php
Route::post('sort', '\AlexCrawford\Sortable\SortableController@sort');
```
--------------------------------
### Move Eloquent Model Entity Position
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Use the `moveBefore($entity)` and `moveAfter($entity)` methods to reorder entities. The position is automatically saved.
```php
$entity = Article::find(1);
$positionEntity = Article::find(10)
$entity->moveAfter($positionEntity);
// if $positionEntity->position is aaa, then $entity->position is aab now
```
--------------------------------
### Create Post-Tag Pivot Table Migration
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Defines the 'post_tag' pivot table with a 'position' column for MySQL/MariaDB, requiring binary collation for correct sorting. For SQLite/PostgreSQL, a simple string column suffices.
```php
Schema::create('post_tag', function (Blueprint $table) {
$table->unsignedInteger('post_id');
$table->unsignedInteger('tag_id');
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin'); // MySQL/MariaDB
// or for SQLite/PostgreSQL: $table->string('position');
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->primary(['post_id', 'tag_id']);
});
```
--------------------------------
### Define MorphToSortedMany Relationship in Video Model
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Establishes a polymorphic many-to-many relationship for the Video model, similar to the Post model, using the MorphToSortedManyTrait.
```php
use AlexCrawford\Sortable\MorphToSortedManyTrait;
class Video extends Model
{
use MorphToSortedManyTrait;
public function tags()
{
return $this->morphToSortedMany(\App\Tag::class, 'taggable');
}
}
```
--------------------------------
### Create Cards Table Migration
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Defines the database schema for the 'cards' table, including a 'position' column with a binary collation suitable for sorting.
```php
// --- Migration ---
Schema::create('cards', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->unsignedInteger('board_column_id');
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin');
});
```
--------------------------------
### Create Taggables Table Migration for Polymorphic Relations
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Sets up the 'taggables' table for polymorphic many-to-many relationships, including a 'position' column with binary collation for MySQL/MariaDB. For SQLite/PostgreSQL, a simple string column is used.
```php
Schema::create('taggables', function (Blueprint $table) {
$table->unsignedInteger('tag_id');
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin'); // MySQL/MariaDB
// or for SQLite/PostgreSQL: $table->string('position');
$table->unsignedInteger('taggable_id');
$table->string('taggable_type');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->index(['taggable_id', 'taggable_type']);
});
```
--------------------------------
### Configure MySQL/MariaDB Position Column for Binary Collation
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
For MySQL/MariaDB, it is critical to specify a binary collation for the 'position' column to ensure correct lexicographic sorting. Default collations may lead to incorrect ordering.
```php
// MySQL/MariaDB - REQUIRED for correct sorting
public function up()
{
Schema::create('articles', function (Blueprint $table) {
// ... other fields ...
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin');
// or use binary type for guaranteed ASCII sorting:
// $table->binary('position', 255);
});
}
```
--------------------------------
### Create Sortable Articles Table Migration
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Define the 'position' column as a string type in your migration. For MySQL/MariaDB, ensure it uses a binary collation like 'utf8mb4_bin' for correct lexicographical sorting.
```php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateArticlesTable extends Migration
{
public function up(): void
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
// SQLite / PostgreSQL:
// $table->string('position');
// MySQL / MariaDB (REQUIRED for correct sorting):
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin');
});
}
}
```
--------------------------------
### PHPStan for Static Analysis
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Executes PHPStan to perform type safety checks on the codebase.
```bash
vendor/bin/phpstan analyse # Run type safety checks
```
--------------------------------
### Register Sortable Route
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Register the HTTP route for handling sorting requests in your routes/web.php file. This route should be protected by appropriate middleware, such as authentication.
```php
Route::post('sort', [\AlexCrawford\Sortable\SortableController::class, 'sort'])
->middleware('auth');
```
--------------------------------
### Reorder Tags for a Post
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Provides methods to reorder tags for a given post, using `moveBefore` or `moveAfter` with the entity to move and the target entity.
```php
$post->tags()->moveBefore($entityToMove, $whereToMoveEntity); // or
$post->tags()->moveAfter($entityToMove, $whereToMoveEntity);
```
--------------------------------
### Post Model with MorphToSortedManyTrait
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Applies the `MorphToSortedManyTrait` to the `Post` model for sorted polymorphic many-to-many relationships. The `morphToSortedMany` method defines the relation and the polymorphic key.
```php
class Post extends Model {
use MorphToSortedManyTrait;
public function tags()
{
return $this->morphToSortedMany('\App\Tag', 'taggable');
}
}
```
--------------------------------
### SortableController
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Provides an HTTP endpoint for handling AJAX sort requests, typically from drag-and-drop interfaces. It reads configuration, validates inputs, and moves entities or relation items.
```APIDOC
## SortableController
### Description
A ready-made Laravel controller action for handling AJAX sort requests from drag-and-drop UIs. Reads from `config/sortable.php`, validates all inputs (including pivot table existence for M2M), and calls `moveAfter`/`moveBefore` on the correct model or relation.
### Setup
1. **Register Service Provider:** Add to `config/app.php`:
```php
'providers' => [
// ...
AlexCrawford\Sortable\SortableServiceProvider::class,
],
```
2. **Publish Config:** Run `php artisan vendor:publish` to create `config/sortable.php`.
3. **Configure Entities:** Define sortable entities in `config/sortable.php`:
```php
return [
'entities' => [
// Simple model sorting
'articles' => \App\Models\Article::class,
// Or explicit array form
'cards' => ['entity' => \App\Models\Card::class],
// Many-to-many pivot sorting
'posts' => [
'entity' => \App\Models\Post::class,
'relation' => 'tags', // method name returning belongsToSortedMany
],
],
];
```
4. **Register Route:** Add to `routes/web.php` (or `routes/api.php`):
```php
Route::post('sort', [
AlexCrawford\Sortable\SortableController::class, 'sort'
])->middleware('auth'); // Optional middleware
```
### Request Payload
**Simple Sort (e.g., `articles`):**
```json
{
"type": "moveAfter", // "moveAfter" | "moveBefore"
"entityName": "articles", // key from config/sortable.php
"id": 3, // entity to move
"positionEntityId": 14 // anchor entity
}
```
**Many-to-Many Sort (e.g., `posts` tags):**
```json
{
"type": "moveAfter",
"entityName": "posts",
"parentId": 7, // the Post id
"id": 3, // Tag id to move
"positionEntityId": 14 // anchor Tag id
}
```
### Response
**Success:**
```json
{"success": true}
```
**Error:**
```json
{"success": false, "errors": {...}, "failed": {...}}
```
### Integration Example (jQuery UI)
**HTML:**
```html
@foreach ($articles as $article)
| ⠿ |
{{ $article->title }} |
@endforeach
```
**JavaScript:**
```javascript
$('.sortable').sortable({
handle: '.handle',
axis: 'y',
update: function (event, ui) {
var $item = ui.item;
var $prev = $item.prev('[data-itemid]');
var $next = $item.next('[data-itemid]');
var payload = {
entityName: $(this).data('entityname'),
id: $item.data('itemid'),
// For M2M, also include: parentId: $item.data('parentid')
};
if ($prev.length) {
$.post('/sort', Object.assign(payload, {
type: 'moveAfter',
positionEntityId: $prev.data('itemid')
}));
} else if ($next.length) {
$.post('/sort', Object.assign(payload, {
type: 'moveBefore',
positionEntityId: $next.data('itemid')
}));
}
}
});
```
### Internal Validation Rules
**Simple:**
- `type`: required | in:moveAfter,moveBefore
- `entityName`: required | in:
- `id`: required | exists:,
- `positionEntityId`: required | exists:,
**Many-to-Many:**
- `parentId`: required | exists:,
- `id`: required | exists:,,,
- `positionEntityId`: required | exists:,,,
```
--------------------------------
### Create Taggables Migration
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Defines the database schema for the pivot table used in polymorphic many-to-many relationships with sorting capabilities. Ensure the 'position' column uses a binary collation for correct sorting.
```php
Schema::create('taggables', function (Blueprint $table) {
$table->unsignedBigInteger('tag_id');
// MySQL/MariaDB:
$table->string('position')->charset('utf8mb4')->collation('utf8mb4_bin');
// SQLite/PostgreSQL: $table->string('position');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->index(['taggable_id', 'taggable_type']);
});
```
--------------------------------
### jQuery UI Sortable Initialization and Update Handler
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Initializes jQuery UI sortable on elements with the 'sortable' class. The 'update' callback handles position changes by sending data to the '/sort' endpoint via AJAX.
```javascript
/**
*
* @param type string 'insertAfter' or 'insertBefore'
* @param entityName
* @param id
* @param positionId
*/
var changePosition = function(requestData){
$.ajax({
'url': '/sort',
'type': 'POST',
'data': requestData,
'success': function(data) {
if (data.success) {
console.log('Saved!');
} else {
console.error(data.errors);
}
},
'error': function(){
console.error('Something wrong!');
}
});
};
$(document).ready(function(){
var $sortableTable = $('.sortable');
if ($sortableTable.length > 0) {
$sortableTable.sortable({
handle: '.sortable-handle',
axis: 'y',
update: function(a, b){
var entityName = $(this).data('entityname');
var $sorted = b.item;
var $previous = $sorted.prev();
var $next = $sorted.next();
if ($previous.length > 0) {
changePosition({
parentId: $sorted.data('parentid'),
type: 'moveAfter',
entityName: entityName,
id: $sorted.data('itemid'),
positionEntityId: $previous.data('itemid')
});
} else if ($next.length > 0) {
changePosition({
parentId: $sorted.data('parentid'),
type: 'moveBefore',
entityName: entityName,
id: $sorted.data('itemid'),
positionEntityId: $next.data('itemid')
});
} else {
console.error('Something wrong!');
}
},
cursor: "move"
});
}
});
```
--------------------------------
### SortableTrait::getNewPosition()
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Calculates a new LexoRank string for a position between two existing ranks, or at the beginning/end of a list, or for an empty list.
```APIDOC
## SortableTrait::getNewPosition()
### Description
Static helper that returns the LexoRank string for a slot between two existing positions. Useful when building custom ordering logic outside of Eloquent events.
### Method Signature
`static getNewPosition(?string $lowerBound = null, ?string $upperBound = null): string`
### Parameters
- **lowerBound** (string|null) - The LexoRank of the item below the desired position. If null, the new position will be before the first item.
- **upperBound** (string|null) - The LexoRank of the item above the desired position. If null, the new position will be after the last item.
### Returns
- (string) - A new LexoRank string that sorts between the provided bounds, or at the beginning/end if bounds are null.
### Examples
```php
// Position between two existing ranks
$between = Article::getNewPosition('U', 'V');
// => e.g., 'UV' (a string that sorts between 'U' and 'V')
// Position after the last item (no upper bound)
$atEnd = Article::getNewPosition('Z');
// => rank after 'Z'
// Position before the first item (no lower bound)
$atStart = Article::getNewPosition(null, 'U');
// => rank before 'U'
// Position in an empty list
$first = Article::getNewPosition(null, null);
// => initial rank, e.g., 'U'
// Manual drag-and-drop handler (without using the built-in controller)
$dragged = Article::find($draggedId);
$prev = Article::find($prevId); // item now above the drop target
$next = Article::find($nextId); // item now below the drop target
$dragged->position = Article::getNewPosition($prev->position, $next->position);
$dragged->save();
```
```
--------------------------------
### Post Model with BelongsToSortedManyTrait
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Integrates the `BelongsToSortedManyTrait` into the `Post` model to define a sorted many-to-many relationship with tags. The `belongsToSortedMany` method takes the related model and optionally the order column.
```php
class Post extends Model {
use BelongsToSortedManyTrait;
public function tags()
{
return $this->belongsToSortedMany('\App\Tag');
}
}
```
--------------------------------
### Register Sortable Service Provider
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Register the SortableServiceProvider in your Laravel application's config/app.php file to enable the sorting functionality.
```php
'providers' => [
// ...
AlexCrawford\Sortable\SortableServiceProvider::class,
]
```
--------------------------------
### Define MorphToSortedMany Relationship in Post Model
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Sets up a polymorphic many-to-many relationship using MorphToSortedManyTrait. The 'position' column is used by default for ordering.
```php
use AlexCrawford\Sortable\MorphToSortedManyTrait;
class Post extends Model
{
use MorphToSortedManyTrait;
public function tags()
{
// Signature: morphToSortedMany($related, $name, $orderColumn = 'position', ...)
return $this->morphToSortedMany(\App\Tag::class, 'taggable');
// Custom pivot order column:
// return $this->morphToSortedMany(\App\Tag::class, 'taggable', 'tag_position');
}
}
```
--------------------------------
### Apply Sorted Scope to Query
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Uses the `sorted()` scope to retrieve models ordered by their position column. This scope can be chained with other query builder methods.
```php
// Basic sorted query
$articles = Article::sorted()->get();
// Chained with other constraints
$published = Article::where('status', 'published')
->sorted()
->paginate(15);
// Sorted within a group
$columnCards = Card::where('board_column_id', 3)
->sorted()
->get();
// Pluck sorted IDs (useful for API responses)
$orderedIds = Article::sorted()->pluck('id');
// => [4, 1, 7, 2, ...]
```
--------------------------------
### Configure Sortable Grouping by Multiple Fields
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
To group entity ordering by multiple fields, define the static property `$sortableGroupField` in your model as an array of field names.
```php
protected static $sortableGroupField = ['fieldName1','fieldName2'];
```
--------------------------------
### Automatic Position Assignment on Create
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
The trait automatically assigns a position to new entities upon creation. You do not need to manually set the 'position' field.
```php
$article = new Article();
$article->title = $faker->sentence(2);
$article->description = $faker->paragraph();
$article->save();
```
--------------------------------
### HTML Table for Sortable Articles
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Use this HTML structure for a sortable list of articles. Ensure the tbody has the 'sortable' class and a 'data-entityname' attribute.
```html
@foreach ($articles as $article)
|
{{{ $article->id }}} |
{{{ $article->title }}} |
@endforeach
```
--------------------------------
### HTML Table for Sortable Tags (Many-to-Many)
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
This HTML structure is for ordering tags associated with a post. It includes 'data-itemId' and 'data-parentId' attributes for relationship tracking.
```html
@foreach ($post->tags as $tag)
|
{{ $tag->id }} |
{{ $tag->title }} |
@endforeach
```
--------------------------------
### Define Sorted Many-to-Many Relationship
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Implements an ordered many-to-many relationship using BelongsToSortedManyTrait. Automatically handles positioning on attach, save, and sync operations.
```php
use AlexCrawford\Sortable\BelongsToSortedManyTrait;
class Post extends Model
{
use BelongsToSortedManyTrait;
public function tags()
{
// Signature: belongsToSortedMany($related, $orderColumn = 'position', $table, ...)
return $this->belongsToSortedMany(\App\Tag::class);
// Custom pivot column name:
// return $this->belongsToSortedMany(\App\Tag::class, 'tag_order');
}
}
```
--------------------------------
### Configure Custom Sortable Field Name
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
If you need to use a column name other than 'position' for sorting, define the static property `$sortableField` in your model.
```php
class Article extends Model
{
use \AlexCrawford\Sortable\SortableTrait;
protected static $sortableField = 'somefield';
}
```
--------------------------------
### Use SortableTrait in Eloquent Model
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Include the `SortableTrait` in your Eloquent model to enable sortable behavior. This trait handles position management automatically.
```php
class Article extends Model
{
use \AlexCrawford\Sortable\SortableTrait;
}
```
--------------------------------
### Prevent Gaps in Position Values on Deletion
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
To prevent gaps in position values after deletion, you can use the `deleting` event to decrement the position of the next model. This requires lexorank-sortable >=2.3.
```php
// YourAppServiceProvider
YourModel::deleting(function ($model) {
$model->next()->decrement('position');
});
```
--------------------------------
### Validate Sorting Request (One-to-Many)
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Use this validator to ensure incoming sorting requests for one-to-many relationships are valid. It checks the move type, entity name, relative entity ID, and the entity ID itself.
```php
$validator = \Validator::make(\Input::all(), array(
'type' => array('required', 'in:moveAfter,moveBefore'), // type of move, moveAfter or moveBefore
'entityName' => array('required', 'in:' . implode(',', array_keys($sortableEntities))), // entity name, 'articles' in this example
'positionEntityId' => 'required|numeric', // id of relative entity
'id' => 'required|numeric', // entity id
));
```
--------------------------------
### Implement SortableTrait in Eloquent Model
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Use the SortableTrait in your Eloquent model to enable automatic position assignment and sorting capabilities. You can optionally override the default 'position' column name.
```php
use Illuminate\Database\Eloquent\Model;
use AlexCrawford\Sortable\SortableTrait;
class Article extends Model
{
use SortableTrait;
// Optional: override the default 'position' column name
protected static $sortableField = 'sort_order';
}
```
--------------------------------
### Configure Sortable Grouping by Single Field
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
To group entity ordering by a specific field, define the static property `$sortableGroupField` in your model with the field name.
```php
protected static $sortableGroupField = 'fieldName';
```
--------------------------------
### jQuery UI Integration for Drag-and-Drop Sorting
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Integrate jQuery UI's sortable widget with your HTML table to enable drag-and-drop functionality. The 'update' callback sends an AJAX request to the '/sort' endpoint with the appropriate payload.
```javascript
'.sortable').sortable({
handle: '.handle',
axis: 'y',
update: function (event, ui) {
var $item = ui.item;
var $prev = $item.prev('[data-itemid]');
var $next = $item.next('[data-itemid]');
var payload = {
entityName: $(this).data('entityname'),
id: $item.data('itemid'),
// For M2M, also include: parentId: $item.data('parentid')
};
if ($prev.length) {
$.post('/sort', Object.assign(payload, {
type: 'moveAfter',
positionEntityId: $prev.data('itemid')
}));
} else if ($next.length) {
$.post('/sort', Object.assign(payload, {
type: 'moveBefore',
positionEntityId: $next.data('itemid')
}));
}
}
});
```
--------------------------------
### Catch SortableException in Laravel
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Catch SortableException when moving entities between different sortable groups. This allows for returning a meaningful error message to the UI.
```php
use AlexCrawford\Sortable\SortableException;
try {
$cardFromTodo->moveAfter($cardFromDone); // different board_column_id
} catch (SortableException $e) {
// "You can't move entities with different sortable group: 1 3"
return response()->json(['error' => $e->getMessage()], 422);
}
```
--------------------------------
### Validate Sorting Request (Many-to-Many)
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
This validator is for many-to-many relationships, including the additional `parentId` field to specify the parent entity for the sorting operation.
```php
$validator = \Validator::make(\Input::all(), array(
'type' => array('required', 'in:moveAfter,moveBefore'), // type of move, moveAfter or moveBefore
'entityName' => array('required', 'in:' . implode(',', array_keys($sortableEntities))), // entity name, 'articles' in this example
'positionEntityId' => 'required|numeric', // id of relative entity
'id' => 'required|numeric', // entity id
'parentId' => 'required|numeric', // parent entity id
));
```
--------------------------------
### SortableController Internal Validation Rules
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
The SortableController automatically applies these validation rules to incoming requests for both simple and many-to-many sorting scenarios. Ensure your payloads adhere to these requirements.
```php
// Simple:
// type required | in:moveAfter,moveBefore
// entityName required | in:
// id required | exists:,
// positionEntityId required | exists:,
//
// M2M:
// parentId required | exists:,
// id required | exists:,,,
// positionEntityId required | exists:,,,
```
--------------------------------
### Add Position Field to Eloquent Model Schema
Source: https://github.com/alexcrawford/lexorank-sortable/blob/main/README.md
Add a 'position' field to your Eloquent model's database schema. This field will store the sortable position.
```php
// schema builder example
public function up()
{
Schema::create('articles', function (Blueprint $table) {
// ... other fields ...
$table->string('position'); // Your model must have position field
});
}
```
--------------------------------
### Define Inverse MorphedBySortedMany Relationship in Tag Model
Source: https://context7.com/alexcrawford/lexorank-sortable/llms.txt
Defines the inverse side of the polymorphic many-to-many relationship, allowing a Tag to reference its associated Posts using morphedBySortedMany.
```php
use AlexCrawford\Sortable\MorphToSortedManyTrait;
class Tag extends Model
{
use MorphToSortedManyTrait;
public function posts()
{
// morphedBySortedMany is the inverse side
return $this->morphedBySortedMany(\App\Post::class, 'taggable');
}
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.