Heim > PHP-Framework > Laravel > Hauptteil

Laravel implementiert die Rückgabe benutzerdefinierter Paging-Informationen mit Resource!

藏色散人
Freigeben: 2022-02-03 04:00:30
nach vorne
3166 Leute haben es durchsucht

Laravel implementiert die Rückgabe benutzerdefinierter Paging-Informationen mit Resource!


Kürzlich eine Idee an das Laravel-Framework übermittelt – Erkennung einer benutzerdefinierten Paginierungsinformationsmethode in PaginatedResourceResponse hinzugefügt, sodass bei Verwendung der Klasse Resource zur Ausgabe von Informationen Es ist sehr praktisch, die Seiteninformationen anzupassen. 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
    }}
Nach dem Login kopieren

这是 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);
    }
Nach dem Login kopieren

看到没,如果当前资源是个分页对象时,它就把任务转向处理分页响应了。接着看:

    /**
     * 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);
    }
Nach dem Login kopieren

噢,它又转给了 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),
        ];
    }
Nach dem Login kopieren

如果你细心的话,你应该能够想到,这里的 $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;
    }
Nach dem Login kopieren

很简单的处理方式,如果对应资源类中有自定义的分页信息组建方法,那就使用它自己的,目前而言,这确实是个好想法。

于此,如何自定义分页信息应该很清晰了。那就是在自己相应的 ResourceCollection 类中添加 paginationInformation

Warum Sie es brauchen

Ich entwickle grundsätzlich APIs. In der Anfangszeit bin ich immer direkt zurückgekehrt, aber diese Methode verursachte manchmal einige Probleme und war umständlich zu warten. Außerdem war es oft notwendig, benutzerdefinierte Felder hinzuzufügen und unterschiedliche Daten für verschiedene Zwecke bereitzustellen, die ich verwendet habe um die zurückgegebenen Daten zu definieren. [Empfohlen: Laravel-Video-Tutorial]🎜🎜Verwenden Sie Ressource Sehr praktisch und kann die Logik klar machen. Es hat jedoch einen Nachteil: Es gibt zu viele paginierte Informationen. Bei API-Projekten werden in den meisten Fällen viele Felder in den Standardausgabe-Paging-Informationen nicht benötigt. Da sie häufig mit einigen alten Projekten verbunden sind, muss das alte Datenformat verwendet oder kompatibel gemacht werden. Die Felder der Paging-Informationen sind Ganz anders: Es gibt keine Möglichkeit, die standardmäßig zurückgegebenen Paging-Informationen direkt zu verwenden. 🎜🎜Ich weiß nicht, wie jeder in ähnlichen Situationen mit Paging-Informationen umgeht, aber um das Ziel zu erreichen, habe ich normalerweise zwei Methoden. Eine besteht darin, Response anzupassen und die Daten neu zu definieren Informationen und zweitens passen Sie alle Ressourcen-bezogenen Klassen an. 🎜🎜Ich weiß nicht viel über die unterste Ebene von Laravel und bin nicht gut in der Entwicklung abstrakter Frameworks, aber nachdem ich das durchgearbeitet habe, habe ich festgestellt, dass die Dinge viel einfacher werden können, wenn Sie kann dies in tun. Beim Erstellen von Paging-Informationen in src/Illuminate/Http/Resources/Json/PaginatedResourceResponse.php können Sie die entsprechenden Komponenten-Paging-Informationen der Klasse Resource verwenden. Sie müssen also nicht jedes Mal viel Geld ausgeben. Hat Zhou Zhang viele Kategorien angepasst? Also habe ich diese Idee dem Laravel-Framework vorgelegt. Dieser Commit wurde zunächst nicht direkt akzeptiert, sondern nach Taylors Anpassungen zusammengeführt und in v8.73.2 veröffentlicht. 🎜🎜Dies ist das erste Mal, dass ich Code zu Laravel beigetragen habe, und es ist auch das erste Mal, dass ich eine Zusammenführungsanfrage an eine so große Codebasis gesendet habe. Obwohl sie nicht direkt übernommen wurde, sind die Ergebnisse aufregend genug. 🎜

Verwendungsbeispiel

🎜Lassen Sie mich also ein einfaches Beispiel für die Verwendung geben. 🎜

Standardausgabe

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'],
        ];
    }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
🎜Dies sind die standardmäßigen Paging-Informationen. Gibt es natürlich viele Felder? Dies reicht aus, um viele Anwendungsszenarien zu bewältigen. Aber manchmal geraten wir dadurch in Schwierigkeiten. Wir brauchen ein wenig Flexibilität. 🎜

Bei Verwendung der ResourceCollection-Klasse

🎜Werfen wir zunächst einen Blick auf die zugrunde liegende Logik! 🎜🎜Wenn eine ResourceCollection vom Controller zurückgegeben wird, wird schließlich deren Methode toResponse aufgerufen, um zu antworten. Dann können Sie diese Methode direkt finden und einen Blick darauf werfen: 🎜
{
    "data": [],
    "page": 1,
    "per_page": 15,
    "total": 5,
    "total_page": 1}
Nach dem Login kopieren
Nach dem Login kopieren
🎜Sehen Sie, wenn die aktuelle Ressource ein Paging-Objekt ist, wird die Aufgabe auf die Verarbeitung der Paging-Antwort verlagert. Nächster Blick: 🎜
public function Index(){
    // ....
    return  SomeResource::collection($paginatedData);}
Nach dem Login kopieren
Nach dem Login kopieren
🎜Oh, es wird wieder nach PaginatedResourceResponse übertragen. Dies ist die Klasse, die wir endlich ändern müssen, da der Inhalt von toResponse zu lang ist Veröffentlichen Sie es hier nicht, hier werden die Antwortdaten natürlich auch verarbeitet, aber es gibt eine unabhängige Methode. Diese Methode ist paginationInformation. Dies ist die Logik vor dem Absenden der PR: 🎜
   /**
     * 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;
            }
        });
    }
Nach dem Login kopieren
Nach dem Login kopieren
🎜Wenn Sie vorsichtig sind, sollten Sie denken können, dass die $this->resource Hier ist es tatsächlich die Instanz von ResourceCollection oben, und ihre Ressource sind unsere Listendaten, bei denen es sich um die Paging-Informationsinstanz handelt. Warum können wir in diesem Fall keine Paging-Informationen in ResourceCollection verarbeiten? Sicher, aber wir mussten etwas hinzufügen, und das ist die Idee, die ich eingereicht habe. 🎜🎜Nach dem Zusammenführen der PR lautet die Logik wie folgt: 🎜rrree🎜Wenn es in der entsprechenden Ressourcenklasse eine benutzerdefinierte Paginierungsinformationskonstruktionsmethode gibt, ist dies in der Tat der Fall eine gute Idee. 🎜🎜An dieser Stelle sollte klar sein, wie die Paging-Informationen angepasst werden können. Das bedeutet, dass Sie die Methode paginationInformation zu Ihrer entsprechenden Klasse ResourceCollection hinzufügen, z. B.: 🎜
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'],
        ];
    }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

这是自定义后的数据输出情况:

{
    "data": [],
    "page": 1,
    "per_page": 15,
    "total": 5,
    "total_page": 1}
Nach dem Login kopieren
Nach dem Login kopieren

结果如我所愿。

使用 Resource 类时

我通常只喜欢定义一个 Resource 类来应对单个对象和列表的情况,这里主要关注如何处理列表数据的分页自定义。

在控制器中,我一般都是这样使用:

public function Index(){
    // ....
    return  SomeResource::collection($paginatedData);}
Nach dem Login kopieren
Nach dem Login kopieren

再来看看 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;
            }
        });
    }
Nach dem Login kopieren
Nach dem Login kopieren

原来它把数据转给了 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'],
        ];
    }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

不过,如果你使用的是 Resource::collection($pageData) 方式,那么还需要额外自定义一个 ResourceCollection 类,并重写对应 Resource 类的 collection 方法。

我通常会定义一个对应的基类,然后其它的都继承它。也可以做个 trait,然后共用。

最后

其实,这个想法我很早就想提交的,但是我一直比较犹豫,这到底是不是一个很大众的需求。不过我最后想明白了,这样做既然能为我节省大量重复且危险的工作,有那么多的开发者,总会有人需要的,所以我提交了,同时也是验证下我的想法到底是否可行,我的做法是否最优,结果当然是我学到了很多,比如写稍微复杂的测试用例。

另外,我想知道大家有没其它方法,或你们是怎么对待不同情况的分页信息的。

最后的最后,你如果也有好的想法,那么尽快提交吧!                                       

Das obige ist der detaillierte Inhalt vonLaravel implementiert die Rückgabe benutzerdefinierter Paging-Informationen mit Resource!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:learnku.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage