Resources
A resource can be either a synonym for your Eloquent model or a combination of the model itself, a resource class, repository, controllers, requests, transformers, actions, and versions.
The resource architecture is designed as follows:
- There's an Eloquent model;
- There's a Repository that operates with the model;
- There's a Resource that defines rules for filtering, sorting, and managing Eloquent relations;
- There's a Controller responsible for handling HTTP requests and responses;
- There's a Resource Manager that manages all data retrieval logic;
- There may also be a Transformer that converts the model (or collection of models) into a response format.
Creating a Resource class
All resource classes must implement the Laniakea\Resources\Interfaces\ResourceInterface
interface. This interface comprises three methods:
getFilters()
- returns an array of filters applicable to the resource;getInclusions()
- returns an array of relations that can be included in the resource;getSorters()
- returns an array of sorters applicable to the resource.
As seen in introduction, a simple resource class might resemble this:
<?php
declare(strict_types=1);
namespace App\Resources;
use App\Resources\Filters\UserEmailFilter;
use App\Resources\Filters\UserNameFilter;
use Laniakea\Resources\Interfaces\ResourceFilterInterface;
use Laniakea\Resources\Interfaces\ResourceInterface;
use Laniakea\Resources\Interfaces\ResourceSorterInterface;
use Laniakea\Resources\Sorters\ColumnSorter;
use Laniakea\Resources\Sorters\VirtualColumnSorter;
class UsersResource implements ResourceInterface
{
/**
* Get available resource filters list.
*
* @return array<string, ResourceFilterInterface|string>
*/
public function getFilters(): array
{
return [
'name' => new UserNameFilter(),
'email' => new UserEmailFilter(),
];
}
/**
* Get available inclusions list.
*
* @return array<string, string[]>
*/
public function getInclusions(): array
{
return [
'posts' => ['posts'],
'posts.comments' => ['posts.comments'],
'avatar' => ['media'],
];
}
/**
* Get available sorters list.
*
* @return array<string, ResourceSorterInterface>
*/
public function getSorters(): array
{
return [
'id' => new ColumnSorter(),
'registered_at' => new VirtualColumnSorter('created_at'),
];
}
}
This resource supports several functionalities:
- Filtering users by name (
UserNameFilter
) and/or email (UserEmailFilter
); - Including user posts (
posts
) or posts with comments (posts.comments
) in the API response; - Sorting users by
id
andregistered_at
fields. Theregistered_at
field is a virtual field mapped to thecreated_at
field in the database.
Let's take a closer look at each of these features.
Filters
Filters are tools for narrowing down the list of resources. They can range from simple operations, such as pushing a criterion to a criteria list, to more complex tasks like performing checks before applying the filter.
You can register as many filters as needed, but they won't be invoked unless there's a query parameter with the same name.
For example, if your resource has two filters (name
and email
), and the current request only includes the email
field in the query string, the name
filter won't be triggered.
Filter Names
The public name (or the name used in the query string) is defined by the array key in the getFilters()
method.
In the above example, there are two filters: name
and email
. The query string for both filters would look like this: https://example.org/users?email=hello&name=John
.
In this case both filters will be triggered and applied to the resource. On the other hand, if the URL is https://example.org/users?email=hello
, only the email
filter will be applied.
Learn more about writing your filters in a separate section.
Inclusions
Inclusions are a means to include related resources in the response. They're defined in the getInclusions()
method and work as follows:
- The array key is the public name of the inclusion;
- The array value is an array of model relationships that should be included.
For example, if you have a User
model that has manyPost
models (via the posts
relationship), you can describe the posts
inclusion and provide a list of model relationships that should be included.
The resource example above has three inclusions: posts
, posts.comments
, and avatar
. The query string for these inclusions would look like this: .
Learn more about how to define inclusions in a separate section.
Sorters
Sorters are used to arrange (or order) the list of resources. They can be simple, just sorting by a column/direction, or more complex, such as joining tables before sorting and then sorting by the joined table's column.
The sorting column and sorting direction are defined by a single query string parameter (by default it's order_by
). Sort by ascending order is as simple as . Append a minus sign to column name to sort resources in descending order: .
Learn more about how to define inclusions in a separate section.
Item criterion
By default, the Resource Manager utilizes the findOrFail
method of the repository to retrieve a single resource.
For instance, when users request a single user by ID, the URL would resemble this: . Resource Manager will then invoke the findOrFail
method of the repository with the ID of 1
.
If your item resolving strategy differs (for example, you might use the uuid
table column and incorporate it into routes instead of auto-incrementing ID), you can define a custom criterion for the Resource Manager.
Implement the Laniakea\Resources\Interfaces\HasItemCriterionInterface
interface and return such a criterion from the getItemCriterion
method of the resource class.
<?php
declare(strict_types=1);
namespace App\Resources;
use App\Repositories\Criteria\ModelIdCriterion;
use Laniakea\Resources\Interfaces\HasItemCriterionInterface;
use Laniakea\Resources\Interfaces\ResourceInterface;
class UsersResource implements ResourceInterface, HasItemCriterionInterface
{
public function getItemCriterion(mixed $id): ModelIdCriterion
{
return new ModelIdCriterion($id);
}
}
Let's assume you have a User
model with a UUID column (uuid
). The URL for the user with UUID 87a92d50-fee1-4df5-8644-2a57881991fa
would be .
The ModelIdCriterion
to retrieve such a user might resemble this:
<?php
declare(strict_types=1);
namespace App\Repositories\Criteria;
use Illuminate\Database\Eloquent\Builder;
use Laniakea\Repositories\Interfaces\RepositoryCriterionInterface;
readonly class ModelIdCriterion implements RepositoryCriterionInterface
{
public function __construct(private string $id)
{
//
}
public function apply(Builder $query): void
{
$query->where('uuid', $this->id);
}
}
Instead of invoking the findOrFail()
method with UUID on UsersRepository
class, Resource Manager will call firstOrFail()
method and apply your ModelIdCriterion
to the query, resulting in correct user retrieval.
And More
There're more features that can be added to the resource class. For example, you can define a global list of inclusions (that should be loaded in all requests, regarding the query string), or define a default sorting column/direction (if the query string doesn't have order_by
parameter).
Each documentation section covers such features in more detail.