This package contains a standard controller that can handle all JSON API endpoints for a resource without any customisation. This means having a controller for a resource is optional. When you register routes, the standard JSON API controller will be used by default.
You can extend the standard controller and use the hooks it provides to customise actions as needed for specific resources. This is useful for dispatching events, jobs, etc on specific resources.
If the standard controller provided by this package does not meet your needs, you can create your own controller as long as it implements the methods expected for the registered routes.
The following route registration:
JsonApi::register('default')->routes(function ($api, $router) {
$api->resource('posts');
});Will use the CloudCreativity\LaravelJsonApi\Http\Controllers\JsonApiController for the posts resource. This
will work for all controller actions without any customisation. So by default, no controller is needed.
Refer to the Routing chapter for details on how to change the default controller.
If you need to customise the controller for a resource, for example to dispatch jobs or events from the controller,
you can extend the JsonApiController. When registering the resource routes you need to specify that a controller
is to be used:
JsonApi::register('default')->withNamespace('Api')->routes(function ($api, $router) {
$api->resource('posts')->controller();
});This will use the PostsController in the Api namespace. If you are using a different name for your controller,
you can specify it as follows:
JsonApi::register('default')->withNamespace('Api')->routes(function ($api, $router) {
$api->resource('posts')->controller('BlogPostsController');
});The
withNamespacemethod is identical to Laravel's namespace method when registering a route group.
Your controller would then look like this:
namespace App\Http\Controllers\Api;
use CloudCreativity\LaravelJsonApi\Http\Controllers\JsonApiController;
class PostsController extends JsonApiController
{
// ...
}By default the controller will execute any modifications (i.e. POST, PATCH and DELETE requests) within
a database transaction on the default database connection. If you need to specify a different database connection,
set the $connection property on the controller:
class PostsController extends JsonApiController
{
protected $connection = 'other';
}If you do not want to use transactions, set the $useTransactions property to false:
class PostsController extends JsonApiController
{
protected $useTransactions = false;
}If you need more control than this, overload the
transactionmethod.
The controller allows you to hook into the resource lifecycle by invoking the following methods if they are implemented. These methods allow you to easily implement application specific actions, such as firing events or dispatching jobs.
| Hook | Arguments | Request Class |
|---|---|---|
searching |
request | FetchResources |
searched |
results, request | FetchResources |
reading |
record, request | FetchResource |
didRead |
result, request | FetchResource |
saving |
record, request | CreateResource or UpdateResource |
creating |
request | CreateResource |
updating |
record, request | UpdateResource |
created |
record, request | CreateResource |
saved |
record, request | CreateResource or UpdateResource |
deleting |
record, request | DeleteResource |
deleted |
record, request | DeleteResource |
The request class is the validated request in the
CloudCreativity\LaravelJsonApi\Http\Requestsnamespace.
The searching, searched, reading and didRead hooks are invoked when resource(s) are being accessed,
i.e. a GET request. The searching and searched hooks are invoked when reading any resources
(the index action), while reading and didRead are invoked when reading a specific record
(the read action).
Note that the
didReadhook will receive a result ofnullif the request has filter parameters and there was no matching record.
The creating and created hooks will be invoked when a resource is being created, i.e. a POST request. The
updating and updated hooks are invoked for a PATCH request on an existing resource. The saving and saved
hooks are called for both POST and PATCH requests.
Note that the
savinghook's first argument (the record) will benullwhen creating a resource.
Controller hooks are intended primarily for dispatching events or jobs. If you need to execute logic to fill values into your domain record when creating or updating them, you should use Adapter hooks instead. This is because adapters are the classes that contain the logic to fill domain records.
The controller also allows you to hook into the relationship lifecycle by invoking the following methods if they are implemented. These methods allow you to easily implement application specific actions, such as firing events or dispatching jobs.
| Hook | Arguments | Request Class |
|---|---|---|
readingRelationship |
record, request | FetchRelated or FetchRelationship |
reading{Field} |
record, request | FetchRelated or FetchRelationship |
didRead{Field} |
record, related, request | FetchRelated or FetchRelationship |
didReadRelationship |
record, related, request | FetchRelated or FetchRelationship |
replacing |
record, request | UpdateRelationship |
replacing{Field} |
record, request | UpdateRelationship |
replaced{Field} |
record, request | UpdateRelationship |
replaced |
record, request | UpdateRelationship |
adding |
record, request | UpdateRelationship |
adding{Field} |
record, request | UpdateRelationship |
added{Field} |
record, request | UpdateRelationship |
added |
record, request | UpdateRelationship |
removing |
record, request | UpdateRelationship |
removing{Field} |
record, request | UpdateRelationship |
removed{Field} |
record, request | UpdateRelationship |
removed |
record, request | UpdateRelationship |
In the above method names {Field} refers to the camel-cased JSON API field name for the relationship. For example,
if reading the author relationship on a posts resource, the readingRelationship and readingAuthor
methods will be invoked if they exist.
The reading... and didRead... methods are invoked when accessing the related resource or the relationship data,
i.e. a GET relationship request. The replacing... and replaced... methods are invoked when changing the
entire relationship in a PATCH relationship request.
For to-many relationships, the adding... and added... methods are invoked when adding resources to the
relationship using a POST relationship request. The removing... and removed... methods are invoked when
removing resource from the relationship using a DELETE relationship request.
The standard controller returns responses for each controller action that comply with the JSON API specification
and are appropriate for the vast majority of use cases. If you need to return a different response, this can
be achieved by returning an instance of Illuminate\Http\Response from a controller hook.
The controller has a
reply()helper method for easily composing JSON API responses. For more information, see the chapter on Responses.
For example, if we wanted to send a 202 Accepted response when a resource was deleted:
/**
* @param App\Post $record
* @return Illuminate\Http\Response
*/
protected function deleted($record)
{
return $this->reply()->meta([
'acceptedAt' => Carbon\Carbon::now(),
], 202);
}This would result in the following HTTP response:
HTTP/1.1 202 Accepted
Content-Type: application/vnd.api+json
{
"meta": {
"acceptedAt": "2018-04-10T11:56:52+00:00"
}
}The Routing Chapter describes how you can register custom routes in your API. For example
if we added an action to share a posts resource:
JsonApi::register('default')->withNamespace('Api')->routes(function ($api) {
$api->resource('posts')->controller()->routes(function ($posts) {
$posts->post('{record}/share', 'share');
});
});This would expect the share method to be implemented on our resource's controller. For example:
namespace App\Http\Controllers\Api;
use CloudCreativity\LaravelJsonApi\Http\Controllers\JsonApiController;
class PostsController extends JsonApiController
{
public function share(\App\Post $post): \Illuminate\Http\Response
{
\App\Jobs\SharePost::dispatch($post);
return $this->reply()->content($post);
}
}When you do this, any query parameters sent by the client will be used when encoding the response. If you have not validated the request, this could result in an error.
To avoid this, you will need to type-hint the JSON API request class to ensure the request is validated. This package provides a number of request classes that validated the different types of request that are defined by the JSON API specification. You should type-hint whichever is appropriate for your action.
These request classes are validated when they are resolved out of the container. I.e. they work like Laravel's form requests.
The example share action does not expect there to be any request body content, but it is going to return
a posts resource in the response. It is therefore the same as request to fetch a posts resource i.e.
GET /api/posts/123. (This is the case even if we have registered the action as needing to be called as
POST /api/posts/123/share.) We would therefore type-hint the FetchResource request object:
namespace App\Http\Controllers\Api;
use CloudCreativity\LaravelJsonApi\Http\Controllers\JsonApiController;
use CloudCreativity\LaravelJsonApi\Http\Requests\FetchResource;
class PostsController extends JsonApiController
{
public function share(FetchResource $request, \App\Post $post): \Illuminate\Http\Response
{
\App\Jobs\SharePost::dispatch($post);
return $this->reply()->content($post);
}
}All request classes are in the CloudCreativity\LaravelJsonApi\Http\Requests namespace. These are
the ones available:
| Action | Request Class |
|---|---|
index |
FetchResources |
create |
CreateResource |
read |
FetchResource |
update |
UpdateResource |
delete |
DeleteResource |
readRelatedResource |
FetchRelated |
readRelationship |
FetchRelationship |
replaceRelationship |
UpdateRelationship |
addToRelationship |
UpdateRelationship |
removeFromRelationship |
UpdateRelationship |
All of these classes extended the
ValidatedRequestabstract class. If none of them do exactly what you need for your custom action, you can write you own request class that extends the abstract class.
If the standard controller does not provide the functionality you require, you are able to write your own controller.
You will need to implement the controller actions listed below. We suggest that you look at the code for this
package's JsonApiController to see how these actions are implemented and what we are type-hinting in each
controller action.
| URL | Controller Action |
|---|---|
GET /posts |
index |
POST /posts |
create |
GET /posts/{record} |
read |
PATCH /posts/{record} |
update |
DELETE /posts/{record} |
delete |
| URL | Controller Action |
|---|---|
GET /posts/{record}/comments |
readRelatedResource |
GET /posts/{record}/relationships/comments |
readRelationship |
PATCH /posts/{record}/relationships/comments |
replaceRelationship |
POST /posts/{record}/relationships/comments |
addToRelationship |
DELETE /posts/{record}/relationships/comments |
removeFromRelationship |