coder-sapient/json-api-document-builder

Json Api Document Builder

v1.0.0 2022-03-28 10:42 UTC

This package is auto-updated.

Last update: 2024-09-28 18:31:56 UTC


README

这个库可以将查询对象解析为JSON:API文档。

功能

  • 使用以下运算符进行分页、排序和过滤(eqneqgtltgteltelike)。
  • 多级嵌套路径资源包含(例如 article, article.author, article.comments.user)。
  • 异步资源包含(Guzzle Promises/A+)。
  • 缓存解析的资源。
  • 完全单元测试

请求示例

GET /api/v1/articles/{id}?include=author
GET /api/v1/articles?include=author,comments.user&page=1&per_page=15
GET /api/v1/articles?sort=id,-title // sort id in asc, title in desc 
GET /api/v1/articles?filter[id]=100,101&filter[title][like]=value

要求

  • PHP版本 >=8.0

安装

使用composer安装包

composer require coder-sapient/json-api-document-builder

基本用法

控制器操作示例

final class ShowArticleAction
{
    public function __construct(private SingleDocumentBuilder $builder)
    {
    }

    public function __invoke(ShowArticleRequest $request): string
    {
        try {
            $document = $this->builder->build($request->toQuery());
        } catch (JsonApiException $e) {
            return json_encode($e->jsonApiErrors());
        }

        return json_encode($document);
    }
}

您可以将以下特质添加到您的请求类中

SingleDocumentRequest

final class ShowArticleRequest extends Request
{
    use SingleDocumentRequest;

    protected function resourceId(): string
    {
        // return from URL ~/articles/{resourceId}  
    }

    protected function resourceType(): string
    {
        return 'articles';
    }

    protected function acceptableIncludes(): array
    {
        return ['author', 'comments', 'comments.user'];
    }
}

DocumentsRequest

final class ListArticlesRequest extends Request
{
    use DocumentsRequest;

    protected function resourceType(): string
    {
        return 'articles';
    }
    
    protected function acceptableSorting(): array
    {
        return ['title', 'created_at'];
    }
    
    protected function acceptableIncludes(): array
    {
        return ['author', 'comments', 'comments.user'];
    }

    protected function acceptableFilters(): array
    {
        return [
            'author_id' => ['eq'],
            'title' => ['eq', 'like'],
        ];
    }
}

Builder

要初始化Builder,您需要提供ResourceResolverFactoryResourceCache实例。

SingleDocumentBuilder

SingleDocumentBuilder扩展Builder

DocumentsBuilder

DocumentsBuilder扩展Builder

Resolver

Factory

ResourceResolverFactory是一个工厂,通过资源类型返回ResourceResolver

interface ResourceResolverFactory
{
    /**
     * @throws ResourceResolverNotFoundException
     */
    public function make(string $resourceType): ResourceResolver;
}

有一个基本实现InMemoryResourceResolverFactory

$factory = new InMemoryResourceResolverFactory();

$factory->add(
    'articles', // resource type
    new ArticleResourceResolver()
);
$factory->add(
    'users', 
    new AuthorResourceResolver()
);
$factory->add(
    'comments',
    new CommentResourceResolver()
);

$builder = new SingleDocumentBuilder($factory, new InMemoryResourceCache());

$singleDocument = $builder->build($request->toQuery());

ResourceResolver

构建器使用ResourceResolver实例根据ID或查询条件查找资源。

interface ResourceResolver
{
    /**
     * @param DocumentsQuery $query
     *
     * @return ResourceObject[]
     */
    public function resolveMany(DocumentsQuery $query): array;

    /**
     * @param SingleDocumentQuery $query
     *
     * @return ResourceObject|null
     */
    public function resolveOne(SingleDocumentQuery $query): ?ResourceObject;

    /**
     * @param string ...$resourceIds
     *
     * @return ResourceObject[]|PromiseInterface
     */
    public function resolveByIds(string ...$resourceIds): array|PromiseInterface;
}

在解析顶级资源集合时,它将提供由过滤器、排序和分页组成的查询条件。您需要使用查询构建器(Doctrine、Eloquent等)与条件匹配。

当尝试包含相关资源并异步加载时,构建器可以接受Guzzle Promises。

PaginationResolver

interface PaginationResolver
{
    /**
     * @param DocumentsQuery $query
     *
     * @return PaginationResponse
     */
    public function paginate(DocumentsQuery $query): PaginationResponse;
}

如果资源解析器实现了PaginationResolver,构建器将在结果文档中添加顶级LinksMeta对象。

{
  "links": {
    "first": "https:///api/v1/articles?page=1&per_page=15",
    "prev": "https:///api/v1/articles?page=1&per_page=15",
    "next": "https:///api/v1/articles?page=2&per_page=15",
    "last": "https:///api/v1/articles?page=3&per_page=15",
  },
  "meta": {
    "total": 45,
    "page": 1,
    "per_page": 15,
    "last_page": 3
  }
}

ResourceCache

构建器使用ResourceCache实例缓存所有解析的资源。

interface ResourceCache
{
    /**
     * @param string $key
     *
     * @return ResourceObject|null
     */
    public function getByKey(string $key): ?ResourceObject;

    /**
     * @return ResourceObject[]
     */
    public function getByKeys(string ...$keys): array;

    /**
     * @param JsonApiQuery $query
     *
     * @return ResourceObject[]
     */
    public function getByQuery(JsonApiQuery $query): array;

    /**
     * @param ResourceObject ...$resources
     *
     * @return void
     */
    public function setByKeys(ResourceObject ...$resources): void;

    /**
     * @param JsonApiQuery $query
     * @param ResourceObject ...$resources
     *
     * @return void
     */
    public function setByQuery(JsonApiQuery $query, ResourceObject ...$resources): void;

    /**
     * @param string ...$keys
     *
     * @return void
     */
    public function removeByKeys(string ...$keys): void;

    /**
     * @param string ...$resourceTypes
     *
     * @return void
     */
    public function removeByTypes(string ...$resourceTypes): void;

    /**
     * @return void
     */
    public function flush(): void;
}

有一个基本实现InMemoryResourceCache。如果您不需要缓存,请使用NullableResourceCache

许可证

麻省理工学院许可证(MIT)。请参阅更多信息许可协议