https://github.com/brainstudnl/json-api-resource
Make your Laravel API JSON:API compliant with the brainstud/json-api-resource package.
https://github.com/brainstudnl/json-api-resource
Last synced: 5 months ago
JSON representation
Make your Laravel API JSON:API compliant with the brainstud/json-api-resource package.
- Host: GitHub
- URL: https://github.com/brainstudnl/json-api-resource
- Owner: brainstudnl
- License: mit
- Created: 2022-03-30T13:11:22.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2025-07-09T09:40:56.000Z (12 months ago)
- Last Synced: 2025-09-19T02:24:53.566Z (9 months ago)
- Language: PHP
- Homepage:
- Size: 256 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# JSON:API Resource for Laravel
Make your Laravel API [JSON:API](https://jsonapi.org/) compliant with the `Brainstud\JsonApi` package.
## Table of contents
- [Installation](#installation)
- [Usage](#usage)
- [Relationships](#relationships)
- [Resource depth](#resource-depth)
- [Exception handler](#exception-handler)
- [Example](#example-usage)
- [Deprecated `register` method](#defining-resources-via-the-register-method)
- [Tweaking responses](#tweak-response)
- [License](#license)
## Installation
Require the package
```bash
composer require brainstud/json-api-resource
```
## Usage
- Let your resource object extend from `JsonApiResource` instead of `JsonResource`.
- Set the type of your resource as a string in `$this->type`.
- For each part of your resource, define the matching `to{resourcePart}` method.
```php
class Resource extends JsonApiResource
{
protected string $type = 'resources';
protected function toAttributes(Request $request): array
{
return [
'field' => $this->resource->field,
'other_field' => $this->resource->other_field,
];
}
protected function toRelationships(Request $request): array
{
return [
'relation' => ['relationMethod', Relation::class],
];
}
protected function toLinks(Request $request): array
{
return [
'type_of_link' => ['href' => 'link'],
];
}
protected function toMeta(Request $request): array
{
return [
'meta' => 'data',
];
}
}
```
## Relationships
[JSON:API: Includes](https://jsonapi.org/format/#fetching-includes)
For the relationships to be included they need to be loaded. This can be done by implementing a `?include` parameter or using [spatie/laravel-query-builder](https://spatie.be/docs/laravel-query-builder/v3/introduction).
## Resource depth
The resource depth has a default of 2. This can be changed by passing an array to the resource where the second item is the required resource depth.
In the following example we use a depth of 3:
```php
public function show(ShowCourseRequest $request, Course $course)
{
$query = (new CoursesQueryBuilder)->find($course->id);
return new CourseResource([$query, 3]);
}
```
Which allows us to ask for an include nested 3 levels deep: `/courses/{identifier}?include=content,content.answers,content.answers.feedback`
## Exception handler
This package contains an exception handler to render exceptions as JSON:API error messages.
Either use this handler directly by editing your `app.php` and registering this singleton
```php
// app.php
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
\Brainstud\JsonApi\Handlers\JsonApiExceptionHandler::class
);
```
Or register your own exception handler and delegate the render to the `JsonApiExceptionHandler::render` method.
```php
// app.php
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
// handler.php
public function render($request, Throwable $exception)
{
if ($request->wantsJson()) {
return (new JsonApiExceptionHandler($this->container))->render($request, $exception);
}
return parent::render($request, $exception);
}
```
### Return error response
There are multiple ways to return an error page
```php
// Throw an exception that will be handled by the JsonApiExceptionHandler
throw new UnprocessableEntityHttpException();
// Return a defined error response
return (new UnprocessableEntityError)->response();
// Return a custom error response
return ErrorResponse::make(new DefaultError(
'PROCESSING_ERROR',
'Could not save item',
'An error occurred during saving of the item'
), Response::HTTP_INTERNAL_SERVER_ERROR);
```
## Example usage
```php
// Course.php
/**
* @property int $id
* @property string $title
* @property string $description
* @property Carbon $created_at
* @property Collection $enrollments
*/
class Course extends Model
{
protected $fillable = [
'title',
'description',
];
public function enrollments(): HasMany
{
return $this->hasMany(Enrollment::class);
}
}
// CourseResource.php
/**
* @property Course $resource
*/
class CourseResource extends JsonApiResource
{
protected string $type = 'courses';
protected function toAttributes(Request $request): array
{
return [
'title' => $this->resource->title,
'description' => $this->resource->description,
'created_at' => $this->resource->created_at->format('c'),
];
}
protected function toRelationships(Request $request): array
{
return [
'enrollments' => ['enrollments', EnrollmentResourceCollection::class],
];
}
protected function toLinks(Request $request): array
{
return [
'view' => ['href' => $this->resource->getShowUrl()],
];
}
protected function toMeta(Request $request): array
{
return [
'enrollments' => $this->resource->enrollments->count(),
];
}
}
// CoursesController.php
class CoursesController
{
public function index(IndexCoursesRequest $request)
{
$query = (new CoursesQueryBuilder)->jsonPaginate();
return new CourseResourceCollection($query);
}
public function show(ShowCourseRequest $request, Course $course)
{
$query = (new CoursesQueryBuilder)->find($course->id);
return new CourseResource($query);
}
}
```
## Defining resources via the `register` method
In the previous version of the package, you would have to define the resource structure via a register method.
This is still possible, but it is **deprecated** and will be removed in a later version.
To use this way of defining a resource, simply define a `register` method in your resource:
```php
protected function register(): array
{
return [
'id' => $this->resource->identifier,
'type' => 'object_type',
'attributes' => [
'field' => $this->resource->field,
'other_field' => $this->resource->other_field,
],
'relationships' => [
'items' => ['items', ItemsResourceCollection::class],
'item' => ['item', ItemResource::class],
],
'meta' => [
'some_data' => 'some value',
],
'links' => [
'some_key' => 'some link',
],
];
}
```
## Tweak response
The `register` method doesn't have access to `$request` like `toArray` of `JsonResource` has.
If you want to manipulate the response based on the request this can be done by overriding the `addToResponse` method.
```php
protected function addToResponse($request, $response): array
{
if ($this->requestWantsMeta($request, 'data')
&& ($data = $this->getData())
) {
$response['meta']['data'] = $data;
}
return $response;
}
```
## License
JsonApi is open-sourced software licensed under the [MIT Licence](LICENSE)