簡體   English   中英

如何在 Laravel 5 中對合並的集合進行分頁?

[英]How can I paginate a merged collection in Laravel 5?

我正在創建一個包含兩種對象的流,BluePerson 和 RedPerson。 為了創建流,我獲取所有兩個對象,然后將它們合並到一個集合中。 這樣做之后,我需要對它們進行分頁,但是分頁似乎是針對雄辯的模型和數據庫查詢,而不是集合。 我已經看到很多關於手動創建分頁器的信息,但是文檔,尤其是 API 中的文檔很少(我什至找不到 Paginator 類接受的參數。)

如何對合並我的收藏的結果進行分頁?

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

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


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

然而 paginate 似乎是用於 eloquent 模型和數據庫查詢,而不是集合。

你是對的。 但是需要一個用於集合的分頁器功能。 頁面

句法:

Collection forPage(int $page, int $perPage)

例子:

休息很簡單。

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);
}

如果您想使用 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;
}

您可以在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('');
        });
    }

然后,您可以從 Collection 調用 paginate,就像 Eloquent 模型一樣。 例如

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

您可以嘗試對兩個集合進行分頁並將它們合並。 您可以在文檔api 中找到有關分頁的更多信息。 這是手動創建自己的分頁器的示例...

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

我在下面包含了 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;
    }
}

分頁集合的最佳方式:

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-從此以后,您可以像代碼一樣對所有集合進行分頁

$people->paginate(5)

我不得不在我正在從事的項目中處理類似的事情,在其中一個頁面中,我必須顯示兩種類型的出版物,這些出版物created_at字段進行分頁排序 就我而言,它是一個Post模型和一個Event模型(以下簡稱出版物)。

唯一的區別是我不想從數據庫中獲取所有出版物然后對結果進行合並和排序,你可以想象如果我們有數百個出版物會引起性能問題。

所以我發現對每個模型進行分頁會更方便,然后才對它們進行合並和排序。

所以這就是我所做的(基於之前發布的答案和評論)

首先讓我向您展示“我的解決方案”的簡化版本,然后我將盡可能多地解釋代碼。

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'));
    }
}

你現在已經猜到了,門面分頁器負責合並和排序我的分頁器( $events$posts

為了使這個答案更加清晰和完整,我將向您展示如何創建自己的 Facade。

你可以選擇把你自己的外牆放在你喜歡的任何地方,我個人選擇把它們放在 app 文件夾下的 Facades 文件夾中,就像這棵樹所示。

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

將此代碼放在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';
    }
}

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

接下來,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;
        });
    }
}

有關更多信息,您可以查看啟動方法

我的分頁器類位於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(),
            ]
        );
    }
}

肯定有改進的空間,所以如果你看到需要改變的東西,請在此處發表評論或在twitter 上聯系我。

嘗試跟隨。

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

您可以像下面這樣更改它:

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

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


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

似乎分頁不再是laravel 8集合的一部分,所以我使用了 laravel 的Illuminate\\Pagination\\Paginator類來對數據進行分頁,但是是一個問題,分頁相關信息正在通過分頁更新,但數據沒有分頁根本!

發現問題了,laravel的Paginator類沒有正確對數據進行分頁,可以查看類的原始方法。

/**
 * Set the items for the paginator.
 *
 * @param  mixed  $items
 * @return void
 */
protected function setItems($items)
{
    $this->items = $items instanceof Collection ? $items : Collection::make($items);

    $this->hasMore = $this->items->count() > $this->perPage;

    $this->items = $this->items->slice(0, $this->perPage);
}

因此,我構建了自己的Paginator類並從 laravel 的Paginator類擴展了它,並解決了我在下面向您展示的問題。

use Illuminate\Support\Collection;

class Paginator extends \Illuminate\Pagination\Paginator
{
    /**
     * Set the items for the paginator.
     *
     * @param  mixed  $items
     * @return void
     */
    protected function setItems($items)
    {
        $this->items = $items instanceof Collection ? $items : Collection::make($items);

        $this->hasMore = $this->items->count() > ($this->perPage * $this->currentPage);

        $this->items = $this->items->slice(
            ($this->currentPage - 1) * $this->perPage,
            $this->perPage
        );
    }
}

類的用法如下

(new Paginator(
    $items,
    $perPage = 10,
    $page = 1, [
        'path' => $request->url(),
    ]
))->toArray(),

我的數據分頁現在工作正常。

我希望這對你有用。

use Illuminate\Support\Collection;

$collection = new Collection;

$collectionA = ModelA::all();
$collectionB = ModelB::all();

$merged_collection = $collectionA->merge($collectionB);

foreach ($merged_collection as $item) {

    $collection->push($item);
}

$paginated_collection = $collection->paginate(10);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM