Recently submitted an idea to the Laravel framework - add detection of a custom paginated information method in PaginatedResourceResponse
so that it can be used Resource
When the class outputs information, it is very convenient to customize the paging information.
I basically develop APIs. In the early days, I always returned directly, but this method sometimes caused some problems and was inconvenient to maintain. In addition, it was often necessary to add custom fields and provide different data for different ends. I have been using it since thenResource
to define the returned data. [Recommended: laravel video tutorial]
Using Resource
is very convenient and can make the logic clear. But it has a disadvantage, that is, there is too much paginated information. For API projects, in most cases, many fields in the default output paging information are not needed, and because they are often connected to some old projects, the old data format needs to be used or made compatible. The fields of the paging information are quite different. , there is no way to directly use the paging information returned by default.
I don’t know how everyone handles paging information in similar situations, but before that, in order to achieve the goal, I usually have two methods, one is to customize Response
, The data information is redefined here, and the second is to customize all the related classes of Resource
.
I don’t know much about the bottom layer of Laravel, and I’m not good at abstract framework development, but after going through this, I found that things can become much simpler. As I explained in the PR, if you can When building pagination information in src/Illuminate/Http/Resources/Json/PaginatedResourceResponse.php
, you can use its component pagination information corresponding to the Resource
class, so you don’t need to do it every time Have you gone to a lot of trouble to customize many categories? So I submitted this idea to the Laravel framework. This commit was not accepted directly at first, but was merged after Taylor's adjustments and released in v8.73.2.
This is the first time I have contributed code to Laravel, and it is also the first time I have submitted a merge request to such a large code base. Although it has not been directly adopted, the results are exciting enough.
So, let me give a simple example of how to use it.
{ "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 }}
This is the paging information output by Laravel by default. There are many fields. Of course, this is enough to cope with many scenarios. But sometimes we get into trouble because of it. We need a little flexibility.
ResourceCollection
classLet’s take a look at the underlying logic first!
When a ResourceCollection
is returned from the controller, its toResponse
method will eventually be called to respond. Then you can directly find this method and take a look:
/** * 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); }
Do you see that if the current resource is a paging object, it will shift the task to processing the paging response. Next look:
/** * 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); }
Oh, it is transferred to PaginatedResourceResponse
again. This is the class we ultimately need to modify. Since the content of toResponse
is too long, it is not here. Posting, anyway, the response data is started here. Of course, the paging information is also processed here, but it has an independent method. This method is paginationInformation
, which is the logic before submitting the 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), ]; }
If you are careful, you should be able to think of $this->resource
In fact, it is an instance of the ResourceCollection
above, then its resource
is our list data, which is the paging information instance. That being the case, why can't we process paging information in ResourceCollection
? Sure, but we needed to add something, and that's the idea I submitted.
After merging PR, its logic is as follows:
/** * 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; }
Very simple processing method. If there is a custom paging information construction method in the corresponding resource class, then use its own , for now, this is indeed a good idea.
At this point, it should be clear how to customize the paging information. That is to add the paginationInformation
method to your corresponding ResourceCollection
class, for example:
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
,然后共用。
其实,这个想法我很早就想提交的,但是我一直比较犹豫,这到底是不是一个很大众的需求。不过我最后想明白了,这样做既然能为我节省大量重复且危险的工作,有那么多的开发者,总会有人需要的,所以我提交了,同时也是验证下我的想法到底是否可行,我的做法是否最优,结果当然是我学到了很多,比如写稍微复杂的测试用例。
另外,我想知道大家有没其它方法,或你们是怎么对待不同情况的分页信息的。
最后的最后,你如果也有好的想法,那么尽快提交吧!
The above is the detailed content of Laravel implements returning custom paging information with Resource!. For more information, please follow other related articles on the PHP Chinese website!