tuyakhov/yii2-json-api

Yii 框架 JSON API 规范的实现

安装数: 87 213

依赖: 1

建议者: 0

安全性: 0

星标: 143

关注者: 18

分支: 18

开放问题: 9

类型:yii2-extension

v1.0.4 2020-06-21 13:53 UTC

README

Yii 框架 JSON API 规范的实现

Latest Stable Version Scrutinizer Code Quality Build Status Total Downloads

安装

通过 composer 安装此扩展是首选方式。

运行

php composer.phar require --prefer-dist tuyakhov/yii2-json-api "*"

或者在您的 composer.json 文件的 require 部分添加

"tuyakhov/yii2-json-api": "*"

to the require section of your composer.json file.

数据序列化和内容协商

控制器

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,
                ],
            ]
        ]);
    }
}

默认情况下,type 的值会自动转换为复数。您可以通过设置 tuyakhov\jsonapi\Serializer::$pluralize 属性来改变此行为

class Controller extends \yii\rest\Controller
{
    public $serializer = [
        'class' => 'tuyakhov\jsonapi\Serializer',
        'pluralize' => false,  // makes {"type": "user"}, instead of {"type": "users"}
    ];
}

定义模型

  1. 让我们定义 User 模型和声明一个 articles 关联
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']);
    }
}
  1. 现在我们需要定义 Article 模型
use tuyakhov\jsonapi\ResourceTrait;
use tuyakhov\jsonapi\ResourceInterface;

class Article extends ActiveRecord implements ResourceInterface
{
    use ResourceTrait;
}
  1. 结果 User 模型将被序列化为正确的 JSON API 资源对象
{
  "data": {
    "type": "users",
    "id": "1",
    "attributes": {
      // ... this user's attributes
    },
    "relationships": {
      "articles": {
        // ... this user's articles
      }
    }
  }
}

控制 JSON API 输出

JSON 响应由 tuyakhov\jsonapi\JsonApiResponseFormatter 类生成,该类将内部使用 yii\helpers\Json 辅助器。此格式化器可以通过不同的选项进行配置,例如 $prettyPrint 选项,这对于开发中更好的可读性响应非常有用,或者 $encodeOptions 以控制 JSON 编码的输出。

格式化器可以在应用程序配置中的 response 应用组件的 yii\web\Response::formatters 属性中进行配置,如下所示

'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,
        ],
    ],
],

链接

您的资源类可能通过实现 LinksInterface 来支持 HATEOAS。该接口包含一个 getLinks() 方法,该方法应返回一个链接列表。通常,您应该返回至少一个代表资源对象自身 URL 的 self 链接。为了使链接出现在关系中,getLinks() 方法应返回 self 链接。基于此链接,每个关系将生成 selfrelated 链接。默认情况下,这是通过在主模型的 self 链接末尾附加关系名称来完成的,您可以简单地通过覆盖 getRelationshipLinks() 方法来改变这种行为。例如,

class User extends ActiveRecord implements ResourceInterface, LinksInterface
{
    use ResourceTrait;
    
    public function getLinks()
    {
        return [
            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
        ];
    }
}

结果

{
  "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"}
    }
  }
}

分页

查询参数家族 page 保留用于分页。此库实现了一种基于页面的策略,并允许使用查询参数,例如 page[number]page[size]
示例: http://yourdomain.com/users?page[number]=3&page[size]=10

启用 JSON API 输入

要使 API 能够接受 JSON API 格式的输入数据,请配置请求应用程序组件的 [[yii\web\Request::$parsers|parsers]] 属性,以使用 [[tuyakhov\jsonapi\JsonApiParser]] 进行 JSON 输入

'request' => [
  'parsers' => [
      'application/vnd.api+json' => 'tuyakhov\jsonapi\JsonApiParser',
  ]
]

默认情况下,它解析 HTTP 请求正文,以便您可以使用用户输入填充模型属性。例如,请求正文

{
  "data": {
    "type": "users",
    "id": "1",
    "attributes": {
        "first-name": "Bob",
        "last-name": "Homster"
    }
  }
}

将被解析为以下数组

// var_dump($_POST);
[
    "User" => [
        "first_name" => "Bob", 
        "last_name" => "Homster"
    ]
]

因此,您可以通过调用 \Yii::$app->request->post() 来访问请求正文,并简单地使用输入数据填充模型

$model = new User();
$model->load(\Yii::$app->request->post());

默认情况下,类型 users 将转换为 User(单数,驼峰式)与模型的 formName() 方法相对应(您可以覆盖它)。您可以覆盖 JsonApiParser::formNameCallback 属性,该属性引用一个将 'type' 成员转换为表单名称的回调。您还可以通过设置 JsonApiParser::memberNameCallback 属性来改变成员名称到变量名称的默认转换行为('first-name' 转换为 'first_name')。

示例

控制器

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',
            ],
        ];
    }
}

模型

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)
        ];
    }
}

配置文件 config/main.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'],
                ],

            ]
        ]
        // ...
    ]
    // ...
]