firevel / sortable
A simple trait to make your Laravel Eloquent models sortable with ease.
Requires
- php: ^8.2
- illuminate/contracts: ^11.0 || ^12.0 || ^13.0
- illuminate/database: ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^11.0 || ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
- phpunit/phpunit: ^10.5 || ^11.0 || ^12.0
README
A simple trait to make your Laravel Eloquent models sortable with ease. Designed for API usage where you can pass sort parameters directly from query strings (e.g., /users?sort=-id).
Installation
Using Composer:
composer require firevel/sortable
Setup
-
Import the
Sortabletrait in your Eloquent model. -
Add a protected
$sortablearray property to your model. This array should list the fields you want to allow for sorting.
Example:
use Firevel\Sortable\Sortable; class User extends Model { use Sortable; /** * Fields allowed for sorting. * * @var array */ protected $sortable = ['id', 'name', 'email']; /** * Default sorting when no sort parameter is provided (optional). * May be an array (['-id']) or a string ('-id'). * * @var array|string|null */ protected $defaultSort = ['-id']; }
Usage
You can now easily sort your models using the sort() query scope.
Ascending Order:
To sort by name in ascending order:
User::sort(['name'])->get();
Descending Order:
To sort by id in descending order:
User::sort(['-id'])->get();
The - sign before the field name indicates descending order.
Multiple Columns:
You can sort by multiple columns at once. The sorting is applied in the order specified:
User::sort(['name', '-id'])->get();
This will sort by name ascending, then by id descending.
API Usage:
This trait is particularly useful for API endpoints where you receive sort parameters from query strings. You can pass the raw JSON:API style string straight to sort() — no need to explode it yourself:
// Example: GET /users?sort=name,-id public function index(Request $request) { return User::sort($request->input('sort'))->get(); }
sort() accepts either a comma-separated string ("name,-id") or an array (['name', '-id']), so both of these are equivalent:
User::sort('name,-id')->get(); User::sort(['name', '-id'])->get();
Whitespace around fields is trimmed, so "name, -id" works too.
Note:
sort()only applies fields listed in the model's$sortableallowlist. Any field that isn't allowed is silently ignored rather than raising an error — so an untrusted?sort=value can never order by an unexpected column. If no valid fields remain, the model's$defaultSort(if any) is applied. To reject invalid input with a validation error instead, use the validation rule below.
Additional Features
Default Sorting
You can define a default sort order that will be applied when no sort parameters are provided:
class User extends Model { use Sortable; protected $sortable = ['id', 'name', 'email', 'created_at']; protected $defaultSort = ['-created_at']; // Sort by newest first by default } // When called with no parameters, uses default sort User::sort([])->get(); // Returns users sorted by created_at DESC
Check if Field is Sortable
You can check if a specific field is sortable using the isSortable() method:
$user = new User(); if ($user->isSortable('name')) { // Field is sortable } if ($user->isSortable('-id')) { // Also works with descending prefix }
Validation Rule for Form Requests
The package provides two ways to validate sort parameters:
String-based validation:
class ListUsersRequest extends FormRequest { public function rules() { return [ 'sort' => ['nullable', 'string', 'sort_fields:App\Models\User'], ]; } }
Object-based validation (provides more detailed error messages):
use Firevel\Sortable\SortField; use App\Models\User; class ListUsersRequest extends FormRequest { public function rules() { return [ 'sort' => ['nullable', 'string', new SortField(User::class)], ]; } }
Both approaches work with string and array inputs:
// String input (e.g., from query string ?sort=name,-id) 'sort' => ['nullable', 'string', 'sort_fields:App\Models\User'] // Array input 'sort' => ['nullable', 'array', 'sort_fields:App\Models\User']
Example usage in a controller:
public function index(ListUsersRequest $request) { return User::sort($request->input('sort'))->get(); }
The validation rule will automatically:
- Check if each field is in the model's
$sortablearray - Handle both ascending (
name) and descending (-name) formats - Provide clear error messages for invalid fields