日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Laravel實現隨著Resource返回自定義分頁信息


最近向 Laravel 框架提交了一個 想法 — 在 PaginatedResourceResponse 中添加一個自定義分頁信息方法的檢測,以便在使用 Resource 類輸出信息時,能夠非常方便地自定義分頁信息。

為什么需要它

我基本上都是在開發 API。早期時候我都是直接返回,但是這種方式有時候會出現一些問題,也不方便維護,加上經常需要添加自定義字段和針對不同端給出不同數據的情況,我后來就一直在使用 Resource 來定義返回的數據。

使用 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返回信息 自定義分頁
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定