Laravel: How to Paginate Collection

in laravel •  5 years ago  (edited)

Laravel: How to Paginate Collection

Photo by Markus Spiske on Unsplash
Photo by Markus Spiske on Unsplash

Laravel supports eloquent model pagination out of the box, but not custom collection. This quick tutorial will show you an easy way to paginate any collections.

Understanding how Laravel Paginator works

If we dive into Laravel’s query builder source code:

// Illuminate/Database/Eloquent/Builder.php

// ...

/**
* Paginate the given query.
*
* @param  int  $perPage
* @param  array  $columns
* @param  string  $pageName
* @param  int|null  $page
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*
* @throws \InvalidArgumentException
*/
public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
    $page = $page ?: Paginator::resolveCurrentPage($pageName);

    $perPage = $perPage ?: $this->model->getPerPage();

    $results = ($total = $this->toBase()->getCountForPagination())
                                ? $this->forPage($page, $perPage)->get($columns)
                                : $this->model->newCollection();

    return $this->paginator($results, $total, $perPage, $page, [
        'path' => Paginator::resolveCurrentPath(),
        'pageName' => $pageName,
    ]);
}

I will briefly explain what Laravel is doing here line by line.

  1. Resolving the current page number in the query metadata.
  2. Resolving the chunking size ($perPage parameter).
  3. Splitting the query results into chunks and get the specific chunk/page that we are after.
  4. Generate a paginator response which includes the pagination metadata and result chunk.

As seen in step 3, Laravel collection class provides a sweet forPage() method, which does the chunking and page retrieving for the paginator. Hmm…Let’s see if we can utilise that.

Creating a helper class

Let’s create our own collection helper class as shown below.

<?php
namespace App\Helpers\General;
use Illuminate\Container\Container;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
class CollectionHelper
{
    public static function paginate(Collection $results, $total, $pageSize)
    {
        $page = Paginator::resolveCurrentPage('page');
        return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [
            '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'
        ));
    }
}

I borrowed the paginator function from Laravel’s query builder class to return the same pagination response as Eloquent. Here’s what happening in our paginate method:

  1. Resolve current page number in the query metadata.

  2. We used the forPage() method to chunk the query results passed into the function. Then we return a pagination response with the other metadata, e.g total, pageSize and etc.

  3. That’s it. We can now use this helper function wherever in our app.

Usage

1. Sorting mutator

If you have included a mutator in your Eloquent model, you may want to sort query results by it. Eloquent doesn’t support mutator sorting, as mutator attributes are created after the query is completed. Good news is we can achieve this by leveraging our helper class that we just created.

Example:

Let’s say we have a full_name mutator in our customer model which combines first_name and last_name together. And we want to sort our query result by full_name. We can achieve this by:


// we use the sortBy method from collection class
$results = Customer::all()->sortBy('full_name');
$total = $results->count();
$pageSize = 20;
$paginated = CollectionHelper::paginate($results, $total, $pageSize);

2. Paginate non eloquent results

They could be merged results obtained from multiple API resources. This is especially common when we are integrating multiple app services together.

Let me know if you have any questions!

Posted from my Medium blog.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://medium.com/@sam_ngu/laravel-how-to-paginate-collection-8cb4b281bc55