最近向 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 instanceof 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实现随着Resource返回自定义分页信息!的详细内容。更多信息请关注PHP中文网其他相关文章!

Laravel通过简化Web开发过程和提供强大功能脱颖而出。其优势包括:1)简洁的语法和强大的ORM系统,2)高效的路由和认证系统,3)丰富的第三方库支持,使得开发者能专注于编写优雅的代码并提高开发效率。

laravelispredminandermanthandermanthandermanthandermanthermanderframework,设计Forserver-SideLogic,databasemagement,andapideplupment,thryitalsosupportsfortfortsfrontenddevelopmentwithbladeTemplates。

Laravel和Python在性能和可扩展性方面的表现各有优劣。Laravel通过异步处理和队列系统提升性能,但受PHP限制在高并发时可能有瓶颈;Python利用异步框架和强大的库生态系统表现出色,但在多线程环境下受GIL影响。

Laravel适合团队熟悉PHP且需功能丰富的项目,Python框架则视项目需求而定。1.Laravel提供优雅语法和丰富功能,适合需要快速开发和灵活性的项目。2.Django适合复杂应用,因其“电池包含”理念。3.Flask适用于快速原型和小型项目,提供极大灵活性。

Laravel可以用于前端开发。1)使用Blade模板引擎生成HTML。2)集成Vite管理前端资源。3)构建SPA、PWA或静态网站。4)结合路由、中间件和EloquentORM创建完整Web应用。

PHP和Laravel可用于构建高效的服务器端应用。1.PHP是开源脚本语言,适用于Web开发。2.Laravel提供路由、控制器、EloquentORM、Blade模板引擎等功能,简化开发。3.通过缓存、代码优化和安全措施,提升应用性能和安全性。4.测试和部署策略确保应用稳定运行。

Laravel和Python在学习曲线和易用性上的表现各有优劣。Laravel适合快速开发Web应用,学习曲线相对平缓,但掌握高级功能需时间;Python语法简洁,学习曲线平缓,但动态类型系统需谨慎。

Laravel在后端开发中的优势包括:1)优雅的语法和EloquentORM简化了开发流程;2)丰富的生态系统和活跃的社区支持;3)提高了开发效率和代码质量。Laravel的设计让开发者能够更高效地进行开发,并通过其强大的功能和工具提升代码质量。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)