For a walkthrough with client-side examples and resource integration, see the
Add sorting how-to.
Route Configuration
To enable sorting for a specific route, you use the sorting() method on the Route object. This defines which fields can be used for sorting in the request.
use apivalk\apivalk\Router\Route\Sort\Sort;
use apivalk\apivalk\Router\Route\Route;
public static function getRoute(): Route
{
return Route::get('/pets')
->sorting([
new Sort('id'),
new Sort('name'),
new Sort('created_at'),
]);
}
By default, an Sort object just defines that a field is “sortable”.
Usage in Controller
When a route has sorting enabled, you can access the resolved sortings from the request via the sorting() method, which returns a SortBag.
The bag is always populated for declared fields. If the client omits order_by, the bag holds the route’s defaults; if the client sends order_by, the requested sorts come first in iteration order, followed by any remaining defaults as tiebreakers.
public function __invoke(ApivalkRequestInterface $request): AbstractApivalkResponse
{
$sorting = $request->sorting();
// Iterate over all sortings (user-requested first, then route defaults).
// Perfect for building an ORDER BY: the user's primary intent comes first.
foreach ($sorting as $field => $sort) {
$direction = $sort->isAsc() ? 'ASC' : 'DESC';
// apply to your query...
}
// Or read a specific field directly via the magic getter.
$direction = $sorting->name->isAsc() ? 'ASC' : 'DESC';
// ...
}
Did the user pick this sort, or is it a default?
Each Sort knows whether it came from the user (?order_by=…) or from the route declaration. Use isRequested() when behavior should depend on user intent:
if ($sorting->status->isRequested()) {
// The user explicitly asked to sort by status.
}
SortBag::getRequested() returns only the user-submitted sorts in the order they were sent — useful when you need user intent without route defaults mixed in:
foreach ($sorting->getRequested() as $sort) {
// Only user-submitted sorts. Empty if the client did not send order_by.
}
has($field) only tells you whether the field is in the bag at all (which, for declared fields, is always true). To check whether the user requested a field, use isRequested() on the corresponding Sort.
Client-Side Usage
Apivalk supports sorting using the order_by query parameter. Clients can specify multiple fields and their sort direction using + (ascending, default) or - (descending) prefixes, separated by commas.
Example: sort by name ascending and then by id descending
GET /pets?order_by=name,-id
Validation
RequestValidationMiddleware rejects any sort field that isn’t declared in the route’s sorting([...]) list. A request like GET /pets?order_by=hacked_field returns 422 Unprocessable Entity with a validation error on the order_by parameter. Routes without any declared sortings skip this check entirely.
OpenAPI Documentation
When you configure sortings on a route, Apivalk automatically generates the corresponding OpenAPI documentation:
- An
order_by query parameter of type string.
- The documentation describes the supported fields and the usage of
+/- prefixes.