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

Posted

技术标签:

【中文标题】Laravel 5.6:自定义分页资源集合元和链接属性【英文标题】:Laravel 5.6: Customise a paginated resource collection meta and links attributes 【发布时间】:2019-04-25 02:20:21 【问题描述】:

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

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

应包括分页信息,例如: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",

【问题讨论】:

我的回答解决了你的问题吗? 【参考方案1】:

我不喜欢 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

您可以使用类似代码的轻量级版本覆盖 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);

【讨论】:

感谢您的彻底回复。您能否详细说明如何执行您提到的这些覆盖?我对您的第二个解决方案最感兴趣,但不确定实施它所需的步骤。我应该在哪里添加新的toResponse 方法? 没关系,我在这个页面的帮助下解决了这个问题 - laravel.com/docs/5.8/eloquent-resources

以上是关于Laravel 5.6:自定义分页资源集合元和链接属性的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 中通过自定义分页器分页方法实现伪静态分页链接以利于 SEO

在 Laravel 5.6 中调用数组上的成员函数 links()

尝试创建自定义日志通道 Laravel 5.6

如何在 laravel 5.6 中创建自定义帮助文件?

Laravel 5分页链接到错误的地方

laravel5自定义分页