7

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.

2
  • 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 them Commented Jan 14, 2021 at 6:00
  • 1
    Does this answer your question? How can I paginate a merged collection in Laravel 5? Commented Sep 9, 2021 at 17:24

6 Answers 6

31

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;
});
Sign up to request clarification or add additional context in comments.

3 Comments

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?
@Shulz means (path, query, fragment, pageName) source
What are "After want to add" and "a paginate of collections"? It seems incomprehensible. Are you using machine translation?
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.

Comments

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

2 Comments

Hi there, trying it right now, but methods forPage and count are "not know" laravel 9, php 8.1.3
i tried with Laravel 9 & php 8.1.7 and it work fine.
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,
        ]
    );
});

Comments

1

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?

Comments

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

2 Comments

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).
OK, the OP has left the building: "Last seen more than 2 years ago"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.