The JSON API specification reserves the sort
query parameter for
sorting resources. Sorting allows clients to
specify the order in which resources are to be returned in responses.
This package provides the following capabilities:
- Validation of
sort
parameters sent by a client. - Automatic conversion of sort parameters to query builder ordering for Eloquent resources.
Sorting is applied when:
- Fetching resources, e.g.
GET /api/posts
. - Fetching related resources, e.g.
GET /api/countries/1/posts
. - Fetching relationship identifiers, e.g.
GET /api/countries/1/relationships/posts
.
As an example, imagine our posts
resource has title
and createdAt
sort parameters.
This request would return posts with the most recently created first (descending order):
GET /api/posts?sort=-createdAt HTTP/1.1
Accept: application/vnd.api+json
This request would return posts that are related to country 1
, sorted by title
ascending, then
createdAt
ascending:
GET /api/countries/1/posts?sort=title,createdAt HTTP/1.1
Accept: application/vnd.api+json
This request would return the resource identifiers of any post that is related to country 1
,
sorted by the post's createdAt
attribute in ascending order:
GET /api/countries/1/relationships/posts?sort=createdAt HTTP/1.1
Accept: application/vnd.api+json
By default validators generated by this package do not allow any sort parameters. This is because the Eloquent adapter automatically converts these parameters to column names for query builder ordering. We therefore expect sort parameters to be whitelisted otherwise the client could provide a path that is not a valid column name.
To allow sort parameters, list them on your resource's validators class using the $allowedSortParameters
property:
namespace App\JsonApi\Posts;
use CloudCreativity\LaravelJsonApi\Validation\AbstractValidators;
class Validators extends AbstractValidators
{
protected $allowedSortParameters = [
'createdAt',
'title',
];
// ...
}
You do not need to list the ascending and descending variations of the sort parameter. For example, if you allow
createdAt
, then we will allow the client to send bothcreatedAt
and-createdAt
.
If the client provides an invalid sort parameter, it will receive the following response:
HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
{
"errors": [
{
"title": "Invalid Query Parameter",
"status": "400",
"detail": "Sort parameter foo is not allowed.",
"source": {
"parameter": "sort"
}
}
]
}
A default sort order can be set on an Eloquent adapter. This will be used when a client provides no sort parameters. For example, if we wanted our posts to be returned in descending created date by default:
class Adapter extends AbstractAdapter
{
protected $defaultSort = '-createdAt';
}
Note that the sort order is the JSON API sort parameter, not the Eloquent column name.
You can also set multiple parameters as the default sort order:
class Adapter extends AbstractAdapter
{
protected $defaultSort = ['-createdAt', 'title'];
}
The Eloquent adapter automatically converts sort parameters into column names, and then applies
these to the query builder instance used when fetching resources or relationships. To do this,
we convert the sort parameter to the snake case form of the name, and then use either orderBy
method on the query builder to apply the sort order (either ascending or descending).
If you have a JSON API sort parameter that does not convert directly to a column name, you can
define the conversion on the $sortColumns
property of your adapter. For example, if the
title
sort parameter has to be applied to the main_title
column:
class Adapter extends AbstractAdapter
{
protected $sortColumns = [
'title' => 'main_title',
];
}
To implement more complex sorting where you need access to the Eloquent query builder instance,
you can add methods to your adapter. The method name must follow the pattern sortBy<Name>
,
where <Name>
is the camel cased name of the JSON API sort field.
For example, if you had a JSON API sort field called likes
, you could implement the
sortByLikes
method on your adapter. This will receive the query builder instance and
the direction of the sort (either asc
or desc
).
class Adapter extends AbstractAdapter
{
// ...
protected function sortByLikes($query, $direction): void
{
$query->withCount('likes')
->orderBy('likes_count', $direction);
}
}