### 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) @endforeach
{{{ $article->id }}} {{{ $article->title }}}
``` -------------------------------- ### 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) @endforeach
{{ $tag->id }} {{ $tag->title }}
``` -------------------------------- ### 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.