Implementation of JSON API specification for the Yii framework ¶
- Installation
- Data Serializing and Content Negotiation:
- Controlling JSON API output
- Links
- Pagination
- Enabling JSON API Input
- Examples
Installation ¶
The preferred way to install this extension is through composer.
Either run
php composer.phar require --prefer-dist tuyakhov/yii2-json-api "*"
or add
"tuyakhov/yii2-json-api": "*"
to the require section of your composer.json
file.
Data Serializing and Content Negotiation: ¶
Controller:
`
php
class Controller extends \yii\rest\Controller
{
public $serializer = 'tuyakhov\jsonapi\Serializer';
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'contentNegotiator' => [
'class' => ContentNegotiator::className(),
'formats' => [
'application/vnd.api+json' => Response::FORMAT_JSON,
],
]
]);
}
}
By default, the value of `type` is automatically pluralized.
You can change this behavior by setting `tuyakhov\jsonapi\Serializer::$pluralize` property:
php
class Controller extends \yii\rest\Controller
{
public $serializer = [
'class' => 'tuyakhov\jsonapi\Serializer',
'pluralize' => false, // makes {"type": "user"}, instead of {"type": "users"}
];
}
Defining models:
1) Let's define `User` model and declare an `articles` relation
php
use tuyakhov\jsonapi\ResourceTrait;
use tuyakhov\jsonapi\ResourceInterface;
class User extends ActiveRecord implements ResourceInterface {
use ResourceTrait;
public function getArticles()
{
return $this->hasMany(Article::className(), ['author_id' => 'id']);
}
}
2) Now we need to define `Article` model
php
use tuyakhov\jsonapi\ResourceTrait;
use tuyakhov\jsonapi\ResourceInterface;
class Article extends ActiveRecord implements ResourceInterface {
use ResourceTrait;
}
3) As the result `User` model will be serialized into the proper json api resource object:
javascript
{
"data": {
"type": "users",
"id": "1",
"attributes": {
// ... this user's attributes
},
"relationships": {
"articles": {
// ... this user's articles
}
}
}
}
`
Controlling JSON API output ¶
The JSON response is generated by the tuyakhov\jsonapi\JsonApiResponseFormatter
class which will
use the yii\helpers\Json
helper internally. This formatter can be configured with different options like
for example the $prettyPrint
option, which is useful on development for
better readable responses, or $encodeOptions
to control the output
of the JSON encoding.
The formatter can be configured in the yii\web\Response::formatters
property of the response
application
component in the application configuration like the following:
'response' => [
// ...
'formatters' => [
\yii\web\Response::FORMAT_JSON => [
'class' => 'tuyakhov\jsonapi\JsonApiResponseFormatter',
'prettyPrint' => YII_DEBUG, // use "pretty" output in debug mode
'encodeOptions' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
],
],
],
Links ¶
Your resource classes may support HATEOAS by implementing the LinksInterface
.
The interface contains getLinks()
method which should return a list of links.
Typically, you should return at least the self
link representing the URL to the resource object itself.
In order to appear the links in relationships getLinks()
method should return self
link.
Based on this link each relationship will generate self
and related
links.
By default it happens by appending a relationship name at the end of the self
link of the primary model,
you can simply change that behavior by overwriting getRelationshipLinks()
method.
For example,
`
php
class User extends ActiveRecord implements ResourceInterface, LinksInterface
{
use ResourceTrait;
public function getLinks()
{
return [
Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
];
}
}
As the result:
javascript
{
"data": {
"type": "users",
"id": "1",
// ... this user's attributes
"relationships": {
"articles": {
// ... article's data
"links": {
"self": {"href": "http://yourdomain.com/users/1/relationships/articles"},
"related": {"href": "http://yourdomain.com/users/1/articles"}
}
}
}
"links": {
"self": {"href": "http://yourdomain.com/users/1"}
}
}
}
`
Pagination ¶
The page
query parameter family is reserved for pagination.
This library implements a page-based strategy and allows the usage of query parameters such as page[number]
and page[size]
Example: http://yourdomain.com/users?page[number]=3&page[size]=10
Enabling JSON API Input ¶
To let the API accept input data in JSON API format, configure the parsers property of the request application component to use the tuyakhov\jsonapi\JsonApiParser for JSON input
`
php
'request' => [
'parsers' => [
'application/vnd.api+json' => 'tuyakhov\jsonapi\JsonApiParser',
]
]
By default it parses a HTTP request body so that you can populate model attributes with user inputs.
For example the request body:
javascript
{
"data": {
"type": "users",
"id": "1",
"attributes": {
"first-name": "Bob",
"last-name": "Homster"
}
}
}
Will be resolved into the following array:
php
// var_dump($_POST);
[
"User" => [
"first_name" => "Bob",
"last_name" => "Homster"
]
]
So you can access request body by calling `\Yii::$app->request->post()` and simply populate the model with input data:
php
$model = new User();
$model->load(\Yii::$app->request->post());
`
By default type users
will be converted into User
(singular, camelCase) which corresponds to the model's formName()
method (which you may override).
You can override the JsonApiParser::formNameCallback
property which refers to a callback that converts 'type' member to form name.
Also you could change the default behavior for conversion of member names to variable names ('first-name' converts into 'first_name') by setting JsonApiParser::memberNameCallback
property.
Examples ¶
Controller:
`
php
class UserController extends \yii\rest\Controller
{
public $serializer = 'tuyakhov\jsonapi\Serializer';
/**
* @inheritdoc
*/
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'contentNegotiator' => [
'class' => ContentNegotiator::className(),
'formats' => [
'application/vnd.api+json' => Response::FORMAT_JSON,
],
]
]);
}
/**
* @inheritdoc
*/
public function actions()
{
return [
'create' => [
'class' => 'tuyakhov\jsonapi\actions\CreateAction',
'modelClass' => ExampleModel::className()
],
'update' => [
'class' => 'tuyakhov\jsonapi\actions\UpdateAction',
'modelClass' => ExampleModel::className()
],
'view' => [
'class' => 'tuyakhov\jsonapi\actions\ViewAction',
'modelClass' => ExampleModel::className(),
],
'delete' => [
'class' => 'tuyakhov\jsonapi\actions\DeleteAction',
'modelClass' => ExampleModel::className(),
],
'view-related' => [
'class' => 'tuyakhov\jsonapi\actions\ViewRelatedAction',
'modelClass' => ExampleModel::className()
],
'update-relationship' => [
'class' => 'tuyakhov\jsonapi\actions\UpdateRelationshipAction',
'modelClass' => ExampleModel::className()
],
'delete-relationship' => [
'class' => 'tuyakhov\jsonapi\actions\DeleteRelationshipAction',
'modelClass' => ExampleModel::className()
],
'options' => [
'class' => 'yii\rest\OptionsAction',
],
];
}
}
Model:
```php
class User extends ActiveRecord implements LinksInterface, ResourceInterface
{
use ResourceTrait;
public function getLinks()
{
$reflect = new \ReflectionClass($this);
$controller = Inflector::camel2id($reflect->getShortName());
return [
Link::REL_SELF => Url::to(["$controller/view", 'id' => $this->getId()], true)
];
}
}
Configuration file config/main.php
:
`
php
return [
// ...
'components' => [
'request' => [
'parsers' => [
'application/vnd.api+json' => 'tuyakhov\jsonapi\JsonApiParser',
]
],
'response' => [
'format' => \yii\web\Response::FORMAT_JSON,
'formatters' => [
\yii\web\Response::FORMAT_JSON => 'tuyakhov\jsonapi\JsonApiResponseFormatter'
]
],
'urlManager' => [
'rules' => [
[
'class' => 'tuyakhov\jsonapi\UrlRule',
'controller' => ['user'],
],
]
]
// ...
]
// ...
]
`
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.