laravel实现随着Resource返回自定义分页信息!
发布时间 - 2022-02-03 00:00:00 点击率:次最近向 Laravel 框架提交了一个 想法 — 在 PaginatedResourceResponse 中添加一个自定义分页信息方法的检测,以便在使用 Resource 类输出信息时,能够非常方便地自定义分页信息。
为什么需要它
我基本上都是在开发 API。早期时候我都是直接返回,但是这种方式有时候会出现一些问题,也不方便维护,加上经常需要添加自定义字段和针对不同端给出不同数据的情况,我后来就一直在使用 Resource 来定义返回的数据。【推荐:laravel视频教程】
使用 Resource 很方便也能够让逻辑清晰。但它有个不好的地方,那就是分页信息太多了。针对 API 项目而言,大多数情况下,默认输出的分页信息里很多字段并不需要,并且由于经常对接的是一些老项目,需要沿用老的数据格式或者做兼容,分页信息的字段大不相同,没办法直接使用默认返回的分页信息。
我不知道大家是怎么处理类似情况时的分页信息的,但在此之前,为了能够达到目的,我通常有两种做法,一是自定义 Response,在这里面把数据信息进行重新定义,二是将 Resource 相关的类全部自定义一遍。
我对 Laravel 底层并不是很了解,我也不擅长做抽象的框架开发,但是在经历这些之后,我发现事情能够变得简单很多,正如我在 PR 阐述的那样,如果可以在 src/Illuminate/Http/Resources/Json/PaginatedResourceResponse.php 中组建分页信息时,能够使用其对应 Resource 类的组件分页信息,那不就不需要每次大费周章的进行自定义很多类了吗。于是我就提交了这个想法给 Laravel 框架。这个提交在一开始并没有被直接接受,而是在经过 Taylor 调整后被合并,并发布在 v8.73.2。
这是我第一次向 Laravel 贡献代码,也是第一次向这么大的代码库提交合并请求,虽然没有被直接采用,但结果足以振奋人心。
使用示例
那么,我来简单的示例一下如何使用吧。
默认输出
{
"data": [],
"links": {
"first": "http://cooman.cootab-v4.test/api/favicons?page=1",
"last": "http://cooman.cootab-v4.test/api/favicons?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« 上一页",
"active": false
},
{
"url": "http://cooman.cootab-v4.test/api/favicons?page=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "下一页 »",
"active": false
}
],
"path": "http://cooman.cootab-v4.test/api/favicons",
"per_page": 15,
"to": 5,
"total": 5
}}这是 Laravel 默认输出的分页信息,是不是很多字段,当然这足够应对很多场景的使用。但有时候也会因此犯难。我们需要一点灵活。
使用 ResourceCollection 类时
我们先来看看底层逻辑吧!
当在控制器返回一个 ResourceCollection 时,最终会调用其 toResponse 方法以响应。那么可以直接找到该方法看看:
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function toResponse($request)
{
if ($this->resource inst
anceof AbstractPaginator || $this->resource instanceof AbstractCursorPaginator) {
return $this->preparePaginatedResponse($request);
}
return parent::toResponse($request);
}看到没,如果当前资源是个分页对象时,它就把任务转向处理分页响应了。接着看:
/**
* Create a paginate-aware HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
protected function preparePaginatedResponse($request)
{
if ($this->preserveAllQueryParameters) {
$this->resource->appends($request->query());
} elseif (! is_null($this->queryParameters)) {
$this->resource->appends($this->queryParameters);
}
return (new PaginatedResourceResponse($this))->toResponse($request);
}噢,它又转给了 PaginatedResourceResponse ,这是我们最终需要修改的类,由于 toResponse 的内容太长,就不在这里贴出,反正就是在这里开始组建响应的数据,分页信息当然也是在这里面做的处理,不过它有个独立的方法。该方法就是 paginationInformation, 这是在提交 PR 前的逻辑:
/**
* Add the pagination information to the response.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function paginationInformation($request)
{
$paginated = $this->resource->resource->toArray();
return [
'links' => $this->paginationLinks($paginated),
'meta' => $this->meta($paginated),
];
}如果你细心的话,你应该能够想到,这里的 $this->resource 其实就是上面的 ResourceCollection 的实例,那么它的 resource 就是我们的列表数据,也就是分页信息实例。既然如此,那我们为何不能在 ResourceCollection 中进行分页信息的处理呢?当然可以,但我们需要加点东西,这就是我提交的想法。
合并 PR 之后,它的逻辑是这样的:
/**
* Add the pagination information to the response.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function paginationInformation($request)
{
$paginated = $this->resource->resource->toArray();
$default = [
'links' => $this->paginationLinks($paginated),
'meta' => $this->meta($paginated),
];
if (method_exists($this->resource, 'paginationInformation')) {
return $this->resource->paginationInformation($request, $paginated, $default);
}
return $default;
}很简单的处理方式,如果对应资源类中有自定义的分页信息组建方法,那就使用它自己的,目前而言,这确实是个好想法。
于此,如何自定义分页信息应该很清晰了。那就是在自己相应的 ResourceCollection 类中添加 paginationInformation 方法即可,比如:
public function paginationInformation($request, $paginated, $default): array
{
return [
'page' => $paginated['current_page'],
'per_page' => $paginated['per_page'],
'total' => $paginated['total'],
'total_page' => $paginated['last_page'],
];
}这是自定义后的数据输出情况:
{
"data": [],
"page": 1,
"per_page": 15,
"total": 5,
"total_page": 1}结果如我所愿。
使用 Resource 类时
我通常只喜欢定义一个 Resource 类来应对单个对象和列表的情况,这里主要关注如何处理列表数据的分页自定义。
在控制器中,我一般都是这样使用:
public function Index(){
// ....
return SomeResource::collection($paginatedData);}再来看看 collection 方法里做了什么:
/**
* Create a new anonymous resource collection.
*
* @param mixed $resource
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public static function collection($resource)
{
return tap(new AnonymousResourceCollection($resource, static::class), function ($collection) {
if (property_exists(static::class, 'preserveKeys')) {
$collection->preserveKeys = (new static([]))->preserveKeys === true;
}
});
}原来它把数据转给了 ResourceCollection,那么只需要将这个 AnonymousResourceCollection 做个自定义不就可以了。
总结
这是一个很小优化,但是很有用。
在此之前,如果想要随着 Resource 返回自定义分页信息,会比较麻烦,需要自定义很多东西,这样的方式,对老用户而言小菜一碟,但是对新手就可能是件棘手的问题。那么自此之后,无论是老用户还是新手这件事将变得易如反掌。只需要在对应的 ResourceCollection 类中添加 paginationInformation 方法,类似下面这样:
public function paginationInformation($request, $paginated, $default): array
{
return [
'page' => $paginated['current_page'],
'per_page' => $paginated['per_page'],
'total' => $paginated['total'],
'total_page' => $paginated['last_page'],
];
}不过,如果你使用的是 Resource::collection($pageData) 方式,那么还需要额外自定义一个 ResourceCollection 类,并重写对应 Resource 类的 collection 方法。
我通常会定义一个对应的基类,然后其它的都继承它。也可以做个 trait,然后共用。
最后
其实,这个想法我很早就想提交的,但是我一直比较犹豫,这到底是不是一个很大众的需求。不过我最后想明白了,这样做既然能为我节省大量重复且危险的工作,有那么多的开发者,总会有人需要的,所以我提交了,同时也是验证下我的想法到底是否可行,我的做法是否最优,结果当然是我学到了很多,比如写稍微复杂的测试用例。
另外,我想知道大家有没其它方法,或你们是怎么对待不同情况的分页信息的。
最后的最后,你如果也有好的想法,那么尽快提交吧!
# laravel
# 分页
# 自定义
# 这是
# 是在
# 的是
# 都是
# 是个
# 如果你
# 也不
# 在这里
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
浅谈Javascript中的Label语句
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
个人摄影网站制作流程,摄影爱好者都去什么网站?
高端建站三要素:定制模板、企业官网与响应式设计优化
JavaScript如何实现音频处理_Web Audio API如何工作?
Laravel定时任务怎么设置_Laravel Crontab调度器配置
Laravel如何为API生成Swagger或OpenAPI文档
google浏览器怎么清理缓存_谷歌浏览器清除缓存加速详细步骤
如何在IIS7中新建站点?详细步骤解析
晋江文学城电脑版官网 晋江文学城网页版直接进入
Laravel怎么实现微信登录_Laravel Socialite第三方登录集成
如何在阿里云服务器自主搭建网站?
Laravel如何创建自定义Artisan命令?(代码示例)
Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID
Laravel路由怎么定义_Laravel核心路由系统完全入门指南
Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives
Laravel怎么为数据库表字段添加索引以优化查询
如何在阿里云完成域名注册与建站?
如何快速搭建高效WAP手机网站?
JS中对数组元素进行增删改移的方法总结
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
如何在新浪SAE免费搭建个人博客?
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势
Laravel Debugbar怎么安装_Laravel调试工具栏配置指南
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
如何在云虚拟主机上快速搭建个人网站?
如何快速搭建高效简练网站?
C++时间戳转换成日期时间的步骤和示例代码
电商网站制作价格怎么算,网上拍卖流程以及规则?
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法
Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】
Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言
高防服务器租用如何选择配置与防御等级?
手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?
网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?
nginx修改上传文件大小限制的方法
高端云建站费用究竟需要多少预算?
如何打造高效商业网站?建站目的决定转化率
香港服务器如何优化才能显著提升网站加载速度?
中山网站制作网页,中山新生登记系统登记流程?
如何彻底卸载建站之星软件?
香港服务器选型指南:免备案配置与高效建站方案解析


anceof AbstractPaginator || $this->resource instanceof AbstractCursorPaginator) {
return $this->preparePaginatedResponse($request);
}
return parent::toResponse($request);
}