nilportugues / jsonapi-bundle
Symfony 2 & 3 JSON API Transformer Package
Requires
- php: >=5.5.0
- nilportugues/json-api: ^2.4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^1.9
- nilportugues/php_backslasher: ^0.2
- symfony/symfony: ^2.0|^3.0
README
适用于Symfony 2和Symfony 3
安装
步骤1:下载Bundle
打开命令行,进入项目目录,执行以下命令以下载此Bundle的最新稳定版本
$ composer require nilportugues/jsonapi-bundle
步骤2:启用Bundle
然后,通过将其添加到项目中的注册Bundle列表中来启用此Bundle,在项目中的app/AppKernel.php
文件中
<?php // app/AppKernel.php // ... class AppKernel extends Kernel { public function registerBundles() { $bundles = array( // ... new NilPortugues\Symfony\JsonApiBundle\NilPortuguesSymfonyJsonApiBundle(), ); // ... } // ... }
使用
创建映射
映射目录
映射文件应位于app/config/serializer
目录。必须创建此目录。
还可以通过编辑配置文件app/config/config.yml
来自定义并放置在其它位置
# app/config/config.yml nilportugues_json_api: mappings: - "%kernel.root_dir%/config/serializer/" - @AppBundle/Product/config/Mappings
映射文件
JSON-API转换器通过将现有的PHP对象转换为其JSON表示来工作。对于每个对象,都需要一个映射文件。
映射文件必须放置在映射目录中。预期的映射文件格式是.yml
,这将允许您重命名、隐藏和创建与所有数据相关联的链接。
例如,这里有一个相当复杂的Post
对象,以演示其工作原理
$post = new Post( new PostId(9), 'Hello World', 'Your first post', new User( new UserId(1), 'Post Author' ), [ new Comment( new CommentId(1000), 'Have no fear, sers, your king is safe.', new User(new UserId(2), 'Barristan Selmy'), [ 'created_at' => (new DateTime('2015/07/18 12:13:00'))->format('c'), 'accepted_at' => (new DateTime('2015/07/19 00:00:00'))->format('c'), ] ), ] );
所需的映射文件系列
# app/config/serializer/acme_domain_dummy_post.yml mapping: class: Acme\Domain\Dummy\Post alias: Message aliased_properties: author: author title: headline content: body hide_properties: [] id_properties: - postId urls: self: get_post ## @Route name comments: get_post_comments ## @Route name relationships: author: related: get_post_author ## @Route name self: get_post_author_relationship ## @Route name
# app/config/serializer/acme_domain_dummy_value_object_post_id.yml mapping: class: Acme\Domain\Dummy\ValueObject\PostId aliased_properties: [] hide_properties: [] id_properties: - postId urls: self: get_post ## @Route name relationships: comment: self: get_post_comments_relationship ## @Route name
# app/config/serializer/acme_domain_dummy_comment.yml mapping: class: Acme\Domain\Dummy\Comment aliased_properties: [] hide_properties: [] id_properties: - commentId urls: self: get_comment ## @Route name relationships: post: self: get_post_comments_relationship ## @Route name
# app/config/serializer/acme_domain_dummy_value_object_comment_id.yml mapping: class: Acme\Domain\Dummy\ValueObject\CommentId aliased_properties: [] hide_properties: [] id_properties: - commentId urls: self: get_comment ## @Route name relationships: post: self: get_post_comments_relationship ## @Route name
# app/config/serializer/acme_domain_dummy_user.yml mapping: class: Acme\Domain\Dummy\User aliased_properties: [] hide_properties: [] id_properties: - userId urls: self: get_user friends: get_user_friends ## @Route name comments: get_user_comments ## @Route name
# app/config/serializer/acme_domain_dummy_value_object_user_id.yml mapping: class: Acme\Domain\Dummy\ValueObject\UserId aliased_properties: [] hide_properties: [] id_properties: - userId urls: self: get_user ## @Route name friends: get_user_friends ## @Route name comments: get_user_comments ## @Route name
输出API响应
这非常简单,只需从服务容器中获取JsonApiSerializer
的实例,并将对象传递给其serialize()
方法。输出将是有效的JSON-API。
这里是一个从Doctrine仓库中获取Post
对象的示例。
最后,提供了一个辅助特质,JsonApiResponseTrait
,用于编写完全兼容的响应,这些响应包装了原始JSON API Transformer库提供的PSR-7 Response对象。
<?php namespace AppBundle\Controller; use NilPortugues\Symfony\JsonApiBundle\Serializer\JsonApiResponseTrait; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class PostController extends Controller { use JsonApiResponseTrait; /** * @\Symfony\Component\Routing\Annotation\Route("/post/{postId}", name="get_post") * * @param $postId * @return \Symfony\Component\HttpFoundation\Response */ public function getPostAction($postId) { $post = $this->get('doctrine.post_repository')->find($postId); $serializer = $this->get('nil_portugues.serializer.json_api_serializer'); /** @var \NilPortugues\Api\JsonApi\JsonApiTransformer $transformer */ $transformer = $serializer->getTransformer(); $transformer->setSelfUrl($this->generateUrl('get_post', ['postId' => $postId], true)); $transformer->setNextUrl($this->generateUrl('get_post', ['postId' => $postId+1], true)); return $this->response($serializer->serialize($post)); } }
输出
HTTP/1.1 200 OK
Cache-Control: private, max-age=0, must-revalidate
Content-type: application/vnd.api+json
{ "data": { "type": "message", "id": "9", "attributes": { "headline": "Hello World", "body": "Your first post" }, "links": { "self": { "href": "http://example.com/posts/9" }, "comments": { "href": "http://example.com/posts/9/comments" } }, "relationships": { "author": { "links": { "self": { "href": "http://example.com/posts/9/relationships/author" }, "related": { "href": "http://example.com/posts/9/author" } }, "data": { "type": "user", "id": "1" } } } }, "included": [ { "type": "user", "id": "1", "attributes": { "name": "Post Author" }, "links": { "self": { "href": "http://example.com/users/1" }, "friends": { "href": "http://example.com/users/1/friends" }, "comments": { "href": "http://example.com/users/1/comments" } } }, { "type": "user", "id": "2", "attributes": { "name": "Barristan Selmy" }, "links": { "self": { "href": "http://example.com/users/2" }, "friends": { "href": "http://example.com/users/2/friends" }, "comments": { "href": "http://example.com/users/2/comments" } } }, { "type": "comment", "id": "1000", "attributes": { "dates": { "created_at": "2015-08-13T21:11:07+02:00", "accepted_at": "2015-08-13T21:46:07+02:00" }, "comment": "Have no fear, sers, your king is safe." }, "relationships": { "user": { "data": { "type": "user", "id": "2" } } }, "links": { "self": { "href": "http://example.com/comments/1000" } } } ], "links": { "self": { "href": "http://example.com/posts/9" }, "next": { "href": "http://example.com/posts/10" } }, "jsonapi": { "version": "1.0" } }
请求对象
JSON API附带一个辅助请求类,NilPortugues\Api\JsonApi\Http\Request\Request(ServerRequestInterface $request)
,实现了PSR-7 Request接口。使用此请求对象将提供您访问在JSON API中期望的所有交互的权限
JSON API查询参数
- &fields[resource]=field1,field2将只显示给定资源中指定的字段。
- &include=resource显示给定资源的关系。
- &include=resource.resource2显示依赖于resource2的资源关系字段。
- &sort=field1,-field2按field2降序和field1升序排序。
- &sort=-field1,field2按field1降序和field2升序排序。
- &page[number]将按基于页面的分页策略返回当前页的元素。
- &page[size]将按基于页面的分页策略返回元素的总数。
- &page[limit]将按基于偏移的分页策略返回限制。
- &page[offset]将按基于偏移的分页策略返回偏移值。
- &page[cursor]将按基于游标的分页策略返回游标值。
- &filter将返回在过滤器参数中传递的数据。
NilPortugues\Api\JsonApi\Http\Request\Request
根据上述查询参数,Request实现了解析和返回已准备好的数据的辅助方法。
namespace \NilPortugues\Api\JsonApi\Http\Request; class Request { public function __construct(ServerRequestInterface $request = null) { ... } public function getIncludedRelationships() { ... } public function getSort() { ... } public function getPage() { ... } public function getFilters() { ... } public function getFields() { ... } }
响应对象(JsonApiResponseTrait)
以下JsonApiResponseTrait
方法提供以返回正确的标头和可用的HTTP状态码
private function errorResponse($json); private function resourceCreatedResponse($json); private function resourceDeletedResponse($json); private function resourceNotFoundResponse($json); private function resourcePatchErrorResponse($json); private function resourcePostErrorResponse($json); private function resourceProcessingResponse($json); private function resourceUpdatedResponse($json); private function response($json); private function unsupportedActionResponse($json);
与NelmioApiDocBundleBundle集成
NelmioApiDocBundle 是一个非常著名的用于API文档的扩展包。与当前扩展包的集成非常简单。
以下是一个示例,遵循之前提供的 PostContoller::getPostAction()
方法
<?php namespace AppBundle\Controller; use NilPortugues\Symfony\JsonApiBundle\Serializer\JsonApiResponseTrait; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class PostController extends Controller { use JsonApiResponseTrait; /** * Get a Post by its identifier. Will return Post, Comments and User data. * * @Nelmio\ApiDocBundle\Annotation\ApiDoc( * resource=true, * description="Get a Post by its unique id", * ) * * @Symfony\Component\Routing\Annotation\Route("/post/{postId}", name="get_post") * @Sensio\Bundle\FrameworkExtraBundle\Configuration\Method({"GET"}) * * @param $postId * @return \Symfony\Component\HttpFoundation\Response */ public function getPostAction($postId) { $post = $this->get('doctrine.post_repository')->find($postId); $serializer = $this->get('nil_portugues.serializer.json_api_serializer'); /** @var \NilPortugues\Api\JsonApi\JsonApiTransformer $transformer */ $transformer = $serializer->getTransformer(); $transformer->setSelfUrl($this->generateUrl('get_post', ['postId' => $postId], true)); $transformer->setNextUrl($this->generateUrl('get_post', ['postId' => $postId+1], true)); return $this->response($serializer->serialize($post)); } }
以下是推荐添加到 app/config/config.yml
中的配置
#app/config/config.yml nelmio_api_doc: sandbox: authentication: name: access_token delivery: http type: basic custom_endpoint: false enabled: true endpoint: ~ accept_type: ~ body_format: formats: [] default_format: form request_format: formats: json: application/vnd.api+json method: accept_header default_format: json entity_to_choice: false
质量
要在命令行中运行PHPUnit测试,请进入测试目录并执行phpunit。
本库试图遵守 PSR-1,PSR-2,PSR-4 和 PSR-7。
如果您发现遵守方面的疏忽,请通过 Pull Request 提交补丁。
贡献
对本包的贡献总是受欢迎的!
支持
您可以使用以下方式之一与我联系
- 通过 contact@nilportugues.com 发送邮件给我
- 提交一个 问题
- 使用Gitter:
作者
许可
代码库采用 MIT许可证 许可。