Laravel 5.6:自定义分页的资源集合元和链接属性

时间:2018-11-22 11:43:46

标签: php laravel eloquent laravel-5.6 laravel-resource

如何自定义Laravel ResourceCollection元和链接信息。

链接应仅包含上一个,下一个和自身,而不是默认情况下的第一个,最后一个,上一个,下一个。

应包括分页信息,例如:当前页面,总计项目,items_per_page,总计页面,而不是当前页面,最后一页,路径,每页到总计。

这是现在元数据和链接信息在JSON响应中的样子:

gtag('config', 'GA_TRACKING_ID', {
    'custom_map': {'dimension1': 'my_username'}
});
gtag('event', 'my_username_event', {'my_username': '<username of logged in user>'});

..我希望它类似于:

"meta": {
    "currentPage": 2,
    "current_page": 1,
    "from": 1,
    "last_page": 3,
    "path": "http://localhost:8000/api",
    "per_page": 5,
    "to": 5,
    "total": 14
},
"links": {
    "self": "http://localhost:8000/api",
    "first": "http://localhost:8000/api?page=1",
    "last": "http://localhost:8000/api?page=3",
    "prev": null,
    "next": "http://localhost:8000/api?page=2"
}

1 个答案:

答案 0 :(得分:2)

我不喜欢Laravel如何实现分页器和资源,因为它很难完成某些事情,例如您提到的问题。

内部人员

在以所需方式自定义响应之前,首先需要了解如何将ResourceCollections转换为响应。

用于资源收集的原始toResponse方法如下:

public function toResponse($request)
{
    return $this->resource instanceof AbstractPaginator
                ? (new PaginatedResourceResponse($this))->toResponse($request)
                : parent::toResponse($request);
}

如果您进一步研究PaginatedResourceResponse类,您将看到以下代码。

...
protected function paginationLinks($paginated)
{
    return [
        'first' => $paginated['first_page_url'] ?? null,
        'last' => $paginated['last_page_url'] ?? null,
        'prev' => $paginated['prev_page_url'] ?? null,
        'next' => $paginated['next_page_url'] ?? null,
    ];
}
...

protected function meta($paginated)
{
    return Arr::except($paginated, [
        'data', 
        'first_page_url',
        'last_page_url',
        'prev_page_url',
        'next_page_url',
    ]);
}

我建议您完整阅读Illuminate\Http\Resources\Json\PaginatedResourceResponseIlluminate\Http\Resources\Json\ResourceResponse以了解发生了什么。

解决方案1:创建自定义的PaginatedResourceResponse

一种解决方案是创建一个扩展PaginatedResourceResponse并覆盖paginationLinks方法的新类。

看起来像这样:

use Illuminate\Http\Resources\Json\PaginatedResourceResponse;

class CustomPaginatedResourceResponse extends PaginatedResourceResponse
{
    protected function paginationLinks($paginated)
    {
        return [
            'prev' => $paginated['prev_page_url'] ?? null,
            'next' => $paginated['next_page_url'] ?? null,
        ];
    }

    protected function meta($paginated)
    {
        $metaData = parent::meta($paginated);
        return [
            'current_page' => $metaData['current_page'] ?? null,
            'total_items' => $metaData['total'] ?? null,
            'per_page' => $metaData['per_page'] ?? null,
            'total_pages' => $metaData['total'] ?? null,
        ];
    }
}

然后,您可以覆盖toResponse方法,使其看起来像这样:

public function toResponse($request)
{
    return $this->resource instanceof AbstractPaginator
                ? (new CustomPaginatedResourceResponse($this))->toResponse($request)
                : parent::toResponse($request);
}

如果您想进一步自定义响应,则可以考虑覆盖其他方法。

解决方案2:在ResourceCollection中覆盖toResponse

除了覆盖PaginatedResourceResponse之外,您还可以使用类似代码的轻量级版本覆盖ResourceCollection中的toResponse方法,如下所示:

public function toResponse($request)
{
    $data = $this->resolve($request);
    if ($data instanceof Collection) {
        $data = $data->all();
    }

    $paginated = $this->resource->toArray();
    // perform a dd($paginated) to see how $paginated looks like

    $json = array_merge_recursive(
        [
            self::$wrap => $data
        ],
        [
            'links' => [
                'first' => $paginated['first_page_url'] ?? null,
                'last' => $paginated['last_page_url'] ?? null,
                'prev' => $paginated['prev_page_url'] ?? null,
                'next' => $paginated['next_page_url'] ?? null,
            ],
            'meta' => [
                'current_page' => $metaData['current_page'] ?? null,
                'total_items' => $metaData['total'] ?? null,
                'per_page' => $metaData['per_page'] ?? null,
                'total_pages' => $metaData['total'] ?? null,
            ],
        ],
        $this->with($request),
        $this->additional
    );

    $status = $this->resource instanceof Model && $this->resource->wasRecentlyCreated ? 201 : 200;

    return response()->json($json, $status);
}

解决方案3:覆盖withResponse方法

一个更简单但可能不那么强大的选择是,像这样在资源集合上覆盖withResponse

public function withResponse($request, $response)
{
    $data = $response->getData(true);
    $prev = $data['links']['prev'];
    $next = $data['links']['next'];
    $self = $data['links']['self'];
    $data['links'] = compact('prev', 'next', 'self');
    $response->setData($data);
}