Identifier
The ID Field
The LaravelJsonApi\Eloquent\Fields\ID
field describes the ID member of an Eloquent resource. It must be defined in a schema's fields
method - omitting it will cause an exception to be thrown.
The ID is added using the static make
method, for example:
use LaravelJsonApi\Eloquent\Fields\ID;
/**
* @inheritDoc
*/
public function fields(): array
{
return [
ID::make(),
// ...other fields
];
}
Column Name
If no arguments are provided to the make
method, the schema will assume that the column name for the id
value will be the same as the model's route key - i.e. the column name returned by the Model::getRouteKeyName()
method.
If you need to use a different column to the route key name, provide this as the first argument to the make
method. For example:
ID::make('uuid')
Pattern
The ID
field also defines the regex pattern for the resource's id
value. This pattern is used both when registering routes for the resource, and when parsing values provided by the client.
By default, the ID
field pattern is [0-9]+
. Therefore you do not need to do anything if your Eloquent model has numeric ids.
If your model uses UUIDs, call the uuid()
method when registering the ID
field:
ID::make()->uuid()
To use a custom pattern, call the matchAs
method:
ID::make()->matchAs('[A-Z_]+')
By default id
pattern matching is case insensitive. To use case-sensitive matching, call the matchCase
method:
ID::make()->matchAs('[a-z]+')->matchCase()
Client-Generated IDs
The JSON:API specification allows servers to accept client-generated IDs along with a request to create a resource.
To enable client-generated IDs for a resource type, use the clientIds
method on the ID
field. For example:
ID::make()->clientIds()
WARNING
If you enable client-generated IDs for a resource, make sure you always add validation rules for the id
field to your resource request class.
Hash IDs
Laravel JSON:API includes support for encoding model keys to resource ids. This feature can be used to hash model keys using the vinkla/hashids package, allowing you to obscure database primary keys from your JSON:API clients.
Installation
To use this feature you need to install our laravel-json-api/hashids
package. This will also install the vinkla/hashids package (if not already installed), and you will need to publish the config from that package.
In summary, run the following commands:
composer require laravel-json-api/hashids
php artisan vendor:publish --provider="Vinkla\Hashids\HashidsServiceProvider"
Hash ID Field
To use hash IDs, use the LaravelJsonApi\HashIds\HashId
class instead of the ID
class shown above. For example:
use LaravelJsonApi\HashIds\HashId;
/**
* @inheritDoc
*/
public function fields(): array
{
return [
HashId::make(),
// ...other fields
];
}
As with the ID
field, if you need to set the column name to use for the id value, provide it as the first argument to the make()
method:
HashId::make('alternative_id')
Hash ID Connection
The HashId
field will use the default connection from the vinkla/hashids package. If you need to use a different default connection, set the default connection name using the static withDefaultConnection()
method. For example, in the boot()
method of your AppServiceProvider
:
use LaravelJsonApi\HashIds\HashId;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
HashId::withDefaultConnection('alternative');
}
}
TIP
If you have multiple APIs that each use a different hash ID connection, call the static withDefaultConnection()
method in your server's serving()
hook.
If you need to set the connection for a specific schema, this can be done using the useConnection()
method. For example:
HashId::make()->useConnection('posts')
Hash ID Pattern
By default the HashId
field sets the regex pattern for the resource id to [a-zA-Z0-9]+
.
If your implementation uses a minimum length for your hash ids, then you can set this using the withLength()
method. For example, the following will set the ID pattern to [a-zA-Z0-9]{16,}
:
HashId::make()->withLength(16)
If you need to use any other pattern, use the matchAs
method as described above.
Hashing Route Keys
If you are already hashing model keys in the Model::getRouteKey()
method, then you will need to set up your HashId
field to account for this. This is because if no column is set on the field, the JSON:API implementation falls back to getting the value from the Model::getRouteKey()
method, which in this scenario would already be hashed.
You have two choices if you already hash the model's route key. Firstly, you could provide the specific column name to the HashId
field. In this scenario, the value will be read from the column rather than using the getRouteKey()
method. Use this approach as follows:
HashId::make('id')
Alternatively, you can mark the HashId
field as not needing to hash the value it is provided with. Do this using the alreadyHashed()
method:
HashId::make()->alreadyHashed()
Custom ID Encoding
If you want to encode JSON:API resource IDs using some other implementation, this can be easily implemented on your own field using the LaravelJsonApi\Contracts\Schema\IdEncoder
interface.
Extend the ID
field and implement the interface's encode()
and decode()
methods. For example:
namespace App\JsonApi\Fields;
use LaravelJsonApi\Contracts\Schema\IdEncoder;
use LaravelJsonApi\Eloquent\Fields\ID;
class EncodedId extends ID implements IdEncoder
{
/**
* Encode the model key to a JSON:API resource ID.
*
* @param string|int $modelKey
* @return string
*/
public function encode($modelKey): string
{
// encode the model key.
}
/**
* Decode the JSON:API resource ID to a model key (id).
*
* Implementations must return `null` if the value cannot be
* decoded to a model key.
*
* @param string $resourceId
* @return string|int|null
*/
public function decode(string $resourceId)
{
// decode the JSON:API resource id
}
}
Then use your EncodedId
field in your schema instead of the ID
field.
Decoding Client-Generated IDs
If your custom ID field supports Client-Generated IDs, you will also need to overload the fill
method on your custom ID class.
For example:
/**
* Fill the model with the value of the JSON:API field.
*
* @param Model $model
* @param mixed $value
*/
public function fill(Model $model, $value): void
{
if ($decoded = $this->decode($value)) {
parent::fill($model, $decoded);
return;
}
throw new RuntimeException('Resource ID did not decode to a model key.');
}