Installation:
composer require rutorika/sortable
Ensure compatibility with your Laravel version (see version table).
Publish Config (optional):
php artisan vendor:publish --provider="Rutorika\Sortable\SortableServiceProvider"
Configures default behavior (e.g., sortable_column, sortable_group_column).
Apply to a Model:
Use the Sortable trait in your Eloquent model:
use Rutorika\Sortable\Sortable;
class Post extends Model
{
use Sortable;
}
This adds sortable() and sortableGroup() methods.
First Use Case:
$post = Post::find(1);
$post->sortable()->move(2); // Move to position 2
$post->save();
sortable_group_column):
$post->sortableGroup()->move(1, 'group_id'); // Move within group_id
Database Migration:
Ensure your table has a sortable_column (default: sortable). Add it via migration:
Schema::table('posts', function (Blueprint $table) {
$table->integer('sortable')->default(0);
});
Basic Sorting:
$item->sortable()->move(5); // Move to position 5
$item1->sortable()->swapWith($item2);
$sorted = Post::sorted()->get();
Grouped Sorting:
sortable_group_column in config (e.g., category_id).$item->sortableGroup()->move(3, 'category_id');
$sortedGroups = Post::sortedGroup('category_id')->get();
Many-to-Many Sorting:
sortableManyToMany() for pivot tables:
class Tag extends Model
{
use Sortable;
public function posts()
{
return $this->belongsToMany(Post::class)->withPivot('sortable');
}
}
$tag->posts()->sortableManyToMany()->move(1, $postId);
Custom Columns: Override defaults in the model:
class Post extends Model
{
use Sortable;
protected $sortableColumn = 'custom_sort_order';
}
sortable() in API responses to ensure consistent ordering:
return Post::sorted()->get()->sortBy('sortable');
// Example: Backpack CRUD sortable column
CRUD::column('title', 'sortable')->sortable();
class PostObserver
{
public function saved(Post $post)
{
if ($post->wasChanged('sortable')) {
Cache::forget('posts_sorted');
}
}
}
Database Locking:
DB::transaction(function () {
$item->sortable()->move(1);
});
update()) without transactions.Grouped Sorting Quirks:
sortable_group_column is not set, grouped sorting will fail silently. Always validate:
if (!$this->sortableGroupColumn) {
throw new \RuntimeException('Group column not configured.');
}
sortable_group_column has a default value in migrations to avoid NULL issues.Many-to-Many Pitfalls:
sortable column. Example migration:
Schema::create('post_tag', function (Blueprint $table) {
$table->integer('sortable')->default(0);
});
sortableManyToMany()->detach() carefully.Performance:
sorted() on large datasets without pagination:
// Bad: Loads all records
$posts = Post::sorted()->get();
// Good: Paginated
$posts = Post::sorted()->paginate(20);
sortable_column for better performance:
Schema::table('posts', function (Blueprint $table) {
$table->integer('sortable')->default(0);
})->index('sortable');
Silent Failures:
config(['sortable.debug' => true]);
Sortable column not found.Race Conditions:
sortable()->lock() to prevent concurrent edits:
$item->sortable()->lock()->move(3);
Migration Errors:
sortable column exists but is ignored, clear config cache:
php artisan config:clear
Custom Sort Logic:
Override getSortableQuery() in your model:
protected function getSortableQuery()
{
return $this->newQuery()->where('is_active', 1);
}
Events: Listen for sorting events:
Sortable::sorting(function ($model, $position) {
event(new SortingEvent($model, $position));
});
Validation:
Add rules to ensure sortable values are within bounds:
$validator = Validator::make($data, [
'sortable' => 'integer|min:0|max:999',
]);
Testing:
Use SortableTestCase for assertions:
$this->assertSorted($posts, [1, 2, 3]);
How can I help you explore Laravel packages today?