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



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

链接应仅包括prev、next和self,而不是默认情况下的first、last、prev和next。

Meta应包括分页信息,如:current_page,total_items,items_per_page,total_pages而不是current_page、from、last_page、path、per_page、to、total。

这就是JSON响应中的元和链接信息的样子:

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

我希望它像:

"meta": {
"current_page": 1,
"total_items": 15,
"per_page": 5,
"total_pages": 3
},
"links": {
"prev": null,
"next": "http://localhost:8000/api?page=2"
"self": "http://localhost:8000/api",
}

我不喜欢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',
]);
}

我建议你充分阅读IlluminateHttpResourcesJsonPaginatedResourceResponseIlluminateHttpResourcesJsonResourceResponse来了解发生了什么

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

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

所以它看起来像:

use IlluminateHttpResourcesJsonPaginatedResourceResponse;
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

您可以使用类似代码的轻量级版本来覆盖ResourceCollection中的toResponse方法,而不是覆盖PaginatedResourceResponse

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

最新更新