Installation:
composer require raditzfarhan/laravel-sortable:^1.0
No additional configuration is required.
Apply the Trait:
Add use RaditzFarhan\LaravelSortable\Sortable; to your Eloquent model (e.g., Post.php).
class Post extends Model
{
use Sortable;
// Optional: Customize the ordering column (defaults to `sort_order`)
protected $sortable = 'ordering';
}
First Use Case:
$post = Post::find(1);
$post->moveUp(); // Moves the post up in the hierarchy
$post->moveDown(); // Moves the post down
$post->moveToTop(); // Moves to the top
$post->moveToBottom(); // Moves to the bottom
$post->save();
$posts = Post::sorted()->get(); // Orders by the `sortable` column
Basic Sorting:
Use the trait’s built-in methods (moveUp(), moveDown(), moveToTop(), moveToBottom()) to manually adjust positions.
$item = Category::find(5);
$item->moveUp()->save(); // Adjusts the `ordering` column via a query
Batch Sorting:
Use the sorted() scope to order results in queries:
$sortedItems = Product::sorted()->get();
Custom Ordering Logic:
Override the getSortableAttribute() method to modify how the sortable value is retrieved or set:
public function getSortableAttribute($value)
{
return $value ?: 0; // Default to 0 if null
}
Integration with Forms:
// Example: Update position after drag
$.post('/update-sort', { id: itemId, position: newPosition });
public function updateSort(Request $request)
{
$item = Model::find($request->id);
$item->update(['ordering' => $request->position]);
return response()->json(['success' => true]);
}
Nested Sorting: For hierarchical data (e.g., categories with subcategories), extend the trait or use a composite key:
// Example: Custom scope for nested sorting
public function scopeSorted($query, $parentId = null)
{
return $query->where('parent_id', $parentId)->orderBy('ordering');
}
Column Name Assumption:
The package defaults to sort_order. If you override protected $sortable, ensure the column exists in the migration:
Schema::table('posts', function (Blueprint $table) {
$table->integer('ordering')->default(0);
});
Race Conditions:
Manual updates (e.g., via AJAX) may cause duplicate ordering values. Use transactions or unique constraints:
DB::transaction(function () use ($item, $newPosition) {
$item->update(['ordering' => $newPosition]);
});
Performance:
Avoid sorted() on large datasets without pagination or limits. Add a limit() clause:
$posts = Post::sorted()->limit(50)->get();
Lumen Compatibility:
Ensure the RaditzFarhan\LaravelSortable\Sortable trait is autoloaded in bootstrap/app.php:
$app->withFacades();
Check Column Updates:
Verify the ordering column updates correctly by inspecting the database or logging:
public function moveUp()
{
logger()->debug('Moving up: ' . $this->{$this->sortable});
// ... existing logic
}
Query Scope Issues:
If sorted() doesn’t work, ensure the column name matches protected $sortable:
dd(Post::query()->toSql()); // Debug the generated SQL
Mass Assignment:
If using fill() or update(), whitelist the sortable column:
protected $fillable = ['title', 'ordering'];
Custom Sorting Logic:
Override the moveUp(), moveDown(), etc., methods to implement custom rules (e.g., skip certain items):
public function moveUp()
{
if ($this->isLocked()) return false;
// ... default logic
}
Event Hooks:
Listen for eloquent.saving to modify sortable values dynamically:
Post::saved(function ($post) {
if ($post->wasChanged('ordering')) {
// Trigger a cache update or notification
}
});
Composite Sorting:
For multi-column sorting (e.g., category_ordering + parent_id), create a custom scope:
public function scopeSorted($query)
{
return $query->orderBy('parent_id')->orderBy('ordering');
}
How can I help you explore Laravel packages today?