How can I paginate a Laravel collection?
C

6

5

Here is how I am trying to paginate:

$posts = Post::all()->sortByDesc("created_at")->pagination(1);

But I get this error:

Method Illuminate\Database\Eloquent\Collection::pagination does not exist.

Cathode answered 13/1, 2021 at 22:6 Comment(2)
if this is the result you actually need that is just this Post::latest()->paginate(1) no need to load all the records into a collection to then return 1 of themPika
Does this answer your question? How can I paginate a merged collection in Laravel 5?Cytaster
C
7

It is because paginate is a Builder method, not a collection.

You need to create paginator manually. How is described on Manually Creating A Paginator.

Chapell answered 13/1, 2021 at 22:20 Comment(0)
L
29

Creating a helper class

<?php

namespace App\Helpers;

use Illuminate\Container\Container;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;

class PaginationHelper
{
    public static function paginate(Collection $results, $showPerPage)
    {
        $pageNumber = Paginator::resolveCurrentPage('page');

        $totalPageNumber = $results->count();

        return self::paginator($results->forPage($pageNumber, $showPerPage), $totalPageNumber, $showPerPage, $pageNumber, [
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => 'page',
        ]);

    }

    /**
     * Create a new length-aware paginator instance.
     *
     * @param  \Illuminate\Support\Collection  $items
     * @param  int  $total
     * @param  int  $perPage
     * @param  int  $currentPage
     * @param  array  $options
     * @return \Illuminate\Pagination\LengthAwarePaginator
     */
    protected static function paginator($items, $total, $perPage, $currentPage, $options)
    {
        return Container::getInstance()->makeWith(LengthAwarePaginator::class, compact(
            'items', 'total', 'perPage', 'currentPage', 'options'
        ));
    }
}

Open the composer.json file. After want to add a helpers file. Composer has a files key (which is an array of file paths) that you can define inside of autoload:

"autoload": {
    "files": [
        "app/Helpers/PaginationHelper.php"
    ],
    "classmap": [
        "database/seeds",
        "database/factories"
    ],
    "psr-4": {
        "App\\": "app/"
    }
},

Now you have to type this command in the terminal:

composer dump-autoload

Now you can create a paginate of collections like the example below:

Route::get('/test_collect_pagintae', function () {

    $users = \App\User::get();

    $showPerPage = 20;

    $paginated = PaginationHelper::paginate($users, $showPerPage);

    return $paginated;
});
Lithic answered 13/1, 2021 at 23:25 Comment(3)
This is the most clear sample of LengthAware helper implementation I've ever seen. I would like to ask what is $options and how could I use it?Ent
@Ent means (path, query, fragment, pageName) sourceInterrogation
What are "After want to add" and "a paginate of collections"? It seems incomprehensible. Are you using machine translation?Olinger
C
7

It is because paginate is a Builder method, not a collection.

You need to create paginator manually. How is described on Manually Creating A Paginator.

Chapell answered 13/1, 2021 at 22:20 Comment(0)
S
6

You can use this code in app/provider/serviceProvider in method boot:

Collection::macro('paginate', function($perPage, $page = null, $pageName = 'page') {
            $page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
            return new LengthAwarePaginator(
                $this->forPage($page, $perPage), // $items
                $this->count(),                  // $total
                $perPage,
                $page,
                [                                // $options
                    'path' => LengthAwarePaginator::resolveCurrentPath(),
                    'pageName' => $pageName,
                ]
            );
        });
Seleucid answered 13/1, 2021 at 22:6 Comment(2)
Hi there, trying it right now, but methods forPage and count are "not know" laravel 9, php 8.1.3Unquestioned
i tried with Laravel 9 & php 8.1.7 and it work fine.Felicitasfelicitate
W
2

A solution

This is very good solution from simonhamp. Thanks simonhamp

My suggestion is execute ->values() in AppServiceProvider like this below. It is important because when slicing a collection, keys are preserved, and we don't want that for paginator.

Collection::macro('paginate', function ($perPage, $total = null, $page = null, $pageName = 'page') {
    $page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);

    return new LengthAwarePaginator(
        $total ? $this : $this->forPage($page, $perPage)->values(),
        $total ?: $this->count(),
        $perPage,
        $page,
        [
            'path' => LengthAwarePaginator::resolveCurrentPath(),
            'pageName' => $pageName,
        ]
    );
});
Wolcott answered 6/5, 2022 at 12:51 Comment(0)
C
0

You can paginate easily by removing all:

$posts = Post::sortByDesc("created_at")->pagination(1);

In my case, I have to merge multiple collections and then paginate through the merged data, so I have to do something like this:

$items = $items2->merge($items1);
$items = $this->paginate($items);

public function paginate($items, $perPage = 15, $page = null, $options = [])
{
    $page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
    $items = $items instanceof Collection ? $items : Collection::make($items);
    return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}

Reference: Laravel Documentation, How to paginate Laravel collection?

Caelum answered 16/3, 2023 at 11:34 Comment(0)
I
-2

Try this code:


//convert to array
$posts = Post::all()->sortByDesc("created_at")->toArray();

//Create new pagination
$currentPage = LengthAwarePaginator::resolveCurrentPage();
$perPage = 3;
$currentItems = array_slice($posts, $perPage * ($currentPage - 1), $perPage);
//with path of current page
$posts = (new LengthAwarePaginator($currentItems, count($posts ), $perPage, $currentPage))->setPath(route('posts....'));

//Convert array of array to array of object
$posts->each(function ($item, $itemKey) use($posts) {
     $posts[$itemKey] = (Object)$item;
});
Inchworm answered 2/9, 2021 at 16:46 Comment(2)
An explanation would be in order. E.g., what is the idea/gist? From the Help Center: "...always explain why the solution you're presenting is appropriate and how it works". Please respond by editing (changing) your answer, not here in comments (*** *** *** *** *** without *** *** *** *** *** "Edit:", "Update:", or similar - the answer should appear as if it was written today).Olinger
OK, the OP has left the building: "Last seen more than 2 years ago"Olinger

© 2022 - 2024 — McMap. All rights reserved.