php 如何在 Laravel 5 中对合并的集合进行分页?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30420505/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 21:46:51  来源:igfitidea点击:

How can I paginate a merged collection in Laravel 5?

phplaravelpaginationlaravel-5

提问by KinsDotNet

I am creating a stream which contains two types of objects, BluePerson and RedPerson. To create the stream, I fetch all of both objects, then merge them into one collection. After doing so, I need to paginate them, however paginate is for eloquent models and DB queries, and not collections, it seems. I have seen a lot about manually creating a paginator, but the documentation, especially in the API is sparse (I can't even seem to find the arguments the Paginator class accepts.)

我正在创建一个流,其中包含两种类型的对象,BluePerson 和 RedPerson。为了创建流,我获取了所有两个对象,然后将它们合并到一个集合中。这样做之后,我需要对它们进行分页,但是 paginate 似乎是用于 eloquent 模型和数据库查询,而不是集合。我已经看到很多关于手动创建分页器的内容,但是文档,尤其是 API 中的文档很少(我什至找不到分页器类接受的参数。)

How can I paginate the results of merging my collections?

如何对合并收藏的结果进行分页?

public function index()
{
    $bluePerson = BluePerson::all();
    $redPerson = RedPerson::all();

    $people = $bluePerson->merge($redPerson)->sortByDesc('created_at');


    return view('stream.index')->with('people', $people);
}

回答by Michael Holland

If you want to use a LengthAwarePaginator simply instantiate one. As mentioned in the comments of a previous answer you will have to set the path for this. You will also need to make sure you resolve the "currentPage" and set the items to be returned before you instantiate the paginator. This can all be done before/on instantiation. So a function may look something like:

如果您想使用 LengthAwarePaginator 只需实例化一个。如上一个答案的评论中所述,您必须为此设置路径。在实例化分页器之前,您还需要确保解析“currentPage”并设置要返回的项目。这一切都可以在实例化之前/在实例化时完成。所以一个函数可能看起来像:

function paginateCollection($collection, $perPage, $pageName = 'page', $fragment = null)
{
    $currentPage = \Illuminate\Pagination\LengthAwarePaginator::resolveCurrentPage($pageName);
    $currentPageItems = $collection->slice(($currentPage - 1) * $perPage, $perPage);
    parse_str(request()->getQueryString(), $query);
    unset($query[$pageName]);
    $paginator = new \Illuminate\Pagination\LengthAwarePaginator(
        $currentPageItems,
        $collection->count(),
        $perPage,
        $currentPage,
        [
            'pageName' => $pageName,
            'path' => \Illuminate\Pagination\LengthAwarePaginator::resolveCurrentPath(),
            'query' => $query,
            'fragment' => $fragment
        ]
    );

    return $paginator;
}

回答by itachi

however paginate is for eloquent models and DB queries, and not collections, it seems.

然而 paginate 似乎是用于 eloquent 模型和数据库查询,而不是集合。

You are right. but there is ineed a paginator function for collections. forPage

你是对的。但是需要一个用于集合的分页器功能。页面

Syntax:

句法:

Collection forPage(int $page, int $perPage)

Example:

例子:

Rest is simple.

休息很简单。

public function foo()
{
    $collection = collect([1,2,3,4,5,6,7,8,9,0]);
    $items = $collection->forPage($_GET['page'], 5); //Filter the page var
    dd($items);
}

回答by Kelt

You might try paginating both sets and merging them. You can find more information about pagination in the docsand the api. Here is an example of manually creating your own paginator...

您可以尝试对两个集合进行分页并将它们合并。您可以在文档api 中找到有关分页的更多信息。这是手动创建自己的分页器的示例...

$perPage = 20;
$blue = BluePerson::paginate($perPage / 2);
$red = RedPerson::paginate($perPage - count($blue));
$people = PaginationMerger::merge($blue, $red);

I have included the PaginationMerger class below.

我在下面包含了 PaginationMerger 类。

use Illuminate\Pagination\LengthAwarePaginator;

class PaginationMerger
{
    /**
     * Merges two pagination instances
     *
     * @param  Illuminate\Pagination\LengthAwarePaginator $collection1
     * @param  Illuminate\Pagination\LengthAwarePaginator $collection2
     * @return Illuminate\Pagination\LengthAwarePaginator
     */
    static public function merge(LengthAwarePaginator $collection1, LengthAwarePaginator $collection2)
    {
        $total = $collection1->total() + $collection2->total();

        $perPage = $collection1->perPage() + $collection2->perPage();

        $items = array_merge($collection1->items(), $collection2->items());

        $paginator = new LengthAwarePaginator($items, $total, $perPage);

        return $paginator;
    }
}

回答by dushoda

You can add the following code for Collection in the Providers/AppServiceProvider.

您可以在Providers/AppServiceProvider 中为 Collection 添加以下代码。

    // Enable pagination
    if (!Collection::hasMacro('paginate')) {

        Collection::macro('paginate', 
            function ($perPage = 15, $page = null, $options = []) {
            $page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
            return (new LengthAwarePaginator(
                $this->forPage($page, $perPage)->values()->all(), $this->count(), $perPage, $page, $options))
                ->withPath('');
        });
    }

Then, you can call paginate from a Collection, just like an Eloquent model. For example

然后,您可以从 Collection 调用 paginate,就像 Eloquent 模型一样。例如

$pages = collect([1, 2, 3, 4, 5, 6, 7, 8, 9])->paginate(5);

回答by Rohallah Hatami

best way for paginate collection:

分页集合的最佳方式:

1- add this to boot function in \app\Providers\AppServiceProvider

1- 将此添加到 \app\Providers\AppServiceProvider 中的启动功能

       /*
         * use Illuminate\Support\Collection;
         * use Illuminate\Pagination\LengthAwarePaginator;
         *
         * Paginate a standard Laravel Collection.
         *
         * @param int $perPage
         * @param int $total
         * @param int $page
         * @param string $pageName
         * @return array
         */
        Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page') {
            $page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
            return new LengthAwarePaginator(
                $this->forPage($page, $perPage),
                $total ?: $this->count(),
                $perPage,
                $page,
                [
                    'path' => LengthAwarePaginator::resolveCurrentPath(),
                    'pageName' => $pageName,
                ]
            );
        });

2-From hereafter for all collection you can paginate like your code

2-从此以后,您可以像代码一样对所有集合进行分页

$people->paginate(5)

回答by S. M. Shahinul Islam

Try following.

尝试跟随。

$arr = $pets->toArray();
$paginator->make($arr, count($arr), $perPage);

回答by chebaby

I had to deal with something like that in a project i was working on, where in one of the pages i had to display two type of publication paginatedand sortedby the created_atfield. In my case it was a Postmodel and an EventModel (hereinafter referred to as publications).

我不得不处理一个项目我工作,其中一个页面中,我不得不显示两个类型的出版物的类似的东西,分页排序created_at领域。就我而言,它是一个Post模型和一个Event模型(以下简称出版物)。

The only difference is i didn't want to get all the publications from database then merge and sort the results, as you can imagine it would rise a performance issue if we have hundreds of publications.

唯一的区别是我不想从数据库中获取所有出版物然后对结果进行合并和排序,你可以想象如果我们有数百个出版物会引起性能问题。

So i figure out that it would be more convenient to paginate each model and only then, merge and sort them.

所以我发现对每个模型进行分页会更方便,然后才对它们进行合并和排序。

So here is what i did (based on answers and comments posted earlier)

所以这就是我所做的(基于之前发布的答案和评论)

First of all let me show you a simplifiedversion of "my solution", then i will try to explain the code as much as i could.

首先让我向您展示“我的解决方案”的简化版本,然后我将尽可能多地解释代码。

use App\Models\Post;
use App\Models\Event;
use App\Facades\Paginator;


class PublicationsController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $events       = Event::latest()->paginate(5);
        $posts        = Post::latest()->paginate(5);

        $publications = Paginator::merge($events, $posts)->sortByDesc('created_at')->get();

        return view('publications.index', compact('publications'));
    }
}

As you can guess it by now, the facade Paginatoris the responsible of merging and sorting my paginators ($events& $posts)

你现在已经猜到了,门面分页器负责合并和排序我的分页器 ( $events& $posts)

To make this answer a little bit more clear and complete, i will show you how to create your own Facade.

为了使这个答案更加清晰和完整,我将向您展示如何创建自己的 Facade。

You can choose to put your own facades anywhere you like, personally, i choose to put them inside Facades folder under the app folder, just like shown in this tree.

你可以选择把你自己的外墙放在你喜欢的任何地方,我个人选择把它们放在 app 文件夹下的 Facades 文件夹中,就像这棵树所示。

+---app
|   +---Console
|   +---Events
|   +---Exceptions
|   +---Exports
|   +---Facades
|   |   +---Paginator.php
|   |   +---...
|   +---Http
|   |   +---Controllers
.   .   +---...
.   .   .

Put this code inside app/Facades/Paginator.php

把这段代码放在里面 app/Facades/Paginator.php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Paginator extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'paginator';
    }
}

For more info, you can see How Facades Work

有关更多信息,您可以查看Facades 的工作原理

Next, bindpaginator to service container, open app\Providers\AppServiceProvider.php

接下来,paginator绑定到服务容器,打开app\Providers\AppServiceProvider.php

namespace App\Providers;

use App\Services\Pagination\Paginator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->app->bind('paginator', function ($app) {

            return new Paginator;
        });
    }
}

For more info, you can see The Boot Method

有关更多信息,您可以查看启动方法

My Paginator class is under app/Services/Pagination/folder. Again, you can put your classes wherever you like.

我的分页器类在app/Services/Pagination/文件夹下。同样,您可以将课程放在任何您喜欢的地方。

namespace App\Services\Pagination;

use Illuminate\Support\Arr;
use InvalidArgumentException;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;

class Paginator
{
    /**
     * All of the items being paginated.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $items;

    /**
     * The number of items to be shown per page.
     *
     * @var int
     */
    protected $perPage;

    /**
     * The total number of items before slicing.
     *
     * @var int
     */
    protected $total;

    /**
     * The base path to assign to all URLs.
     *
     * @var string
     */
    protected $path = '/';


    /**
     * Merge paginator instances
     *
     * @param  mixed $paginators
     * @param  bool  $descending
     * @return \Illuminate\Pagination\LengthAwarePaginator
     */
    function merge($paginators)
    {
        $paginators = is_array($paginators) ? $paginators : func_get_args();

        foreach ($paginators as $paginator) {
            if (!$paginator instanceof LengthAwarePaginator) {
                throw new InvalidArgumentException("Only LengthAwarePaginator may be merged.");
            }
        }

        $total   = array_reduce($paginators, function($carry, $paginator) {

            return $paginator->total();
        }, 0);

        $perPage = array_reduce($paginators, function($carry, $paginator) {

            return $paginator->perPage();
        }, 0);

        $items   = array_map(function($paginator) {

            return $paginator->items();

        }, $paginators);

        $items         = Arr::flatten($items);

        $items         = Collection::make($items);

        $this->items   = $items;
        $this->perPage = $perPage;
        $this->total   = $total;

        return $this;
    }

    /**
     * Sort the collection using the given callback.
     *
     * @param  callable|string  $callback
     * @param  int  $options
     * @param  bool  $descending
     * @return static
     */
    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
    {
        $this->items = $this->items->sortBy($callback, $options, $descending);

        return $this;
    }

    /**
     * Sort the collection in descending order using the given callback.
     *
     * @param  callable|string  $callback
     * @param  int  $options
     * @return static
     */
    public function sortByDesc($callback, $options = SORT_REGULAR)
    {
        return $this->sortBy($callback, $options, true);
    }

    /**
     * Get paginator
     *
     * @return \Illuminate\Pagination\LengthAwarePaginator
     */
    public function get()
    {
        return new LengthAwarePaginator(
            $this->items,
            $this->total,
            $this->perPage,
            LengthAwarePaginator::resolveCurrentPage(),
            [
                'path' => LengthAwarePaginator::resolveCurrentPath(),
            ]
        );
    }
}


Definitely there is room for improvements, so please if you see something that needs to be changed, leave a comment here or reach me on twitter.

肯定有改进的空间,所以如果你看到需要改变的东西,请在此处发表评论或在twitter 上联系我。