Pagination

CodeIgniter provides a very simple, but flexible pagination library that is simple to theme, works with the model, and capable of supporting multiple paginators on a single page.

Loading the Library

Like all services in CodeIgniter, it can be loaded via Config\Services, though you usually will not need to load it manually:

<?php

$pager = service('pager');

Paginating with Models

In most cases, you will be using the Pager library in order to paginate results that you retrieve from the database. When using the Model class, you can use its built-in paginate() method to automatically retrieve the current batch of results, as well as set up the Pager library so it’s ready to use in your controllers. It even reads the current page it should display from the current URL via a page=X query variable.

To provide a paginated list of users in your application, your controller’s method would look something like:

<?php

namespace App\Controllers;

use App\Models\UserModel;

class UserController extends BaseController
{
    public function index()
    {
        $model = model(UserModel::class);

        $data = [
            'users' => $model->paginate(10),
            'pager' => $model->pager,
        ];

        return view('users/index', $data);
    }
}

In this example, we first create a new instance of our UserModel. Then we populate the data to send to the view. The first element is the results from the database, users, which is retrieved for the correct page, returning 10 users per page. The second item that must be sent to the view is the Pager instance itself. As a convenience, the Model will hold on to the instance it used and store it in the public property, $pager. So, we grab that and assign it to the $pager variable in the view.

Customizing Query for Pagination

To customize a query for pagination in a model, you can add Query Builder methods before paginate() method.

Adding WHERE

If you want to add WHERE conditions, you can specify conditions directly:

use App\Models\UserModel;

// In your Controller.
$model = model(UserModel::class);

$data = [
    'users' => $model->where('ban', 1)->paginate(10),
    'pager' => $model->pager,
];

You can move the conditions to a separate method:

<?php

namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    // ...

    public function banned()
    {
        $this->builder()->where('ban', 1);

        return $this; // This will allow the call chain to be used.
    }
}
use App\Models\UserModel;

// In your Controller.
$model = model(UserModel::class);

$data = [
    'users' => $model->banned()->paginate(10),
    'pager' => $model->pager,
];

Adding JOIN

You can join another table:

<?php

namespace App\Models;

use CodeIgniter\Model;

class NewsModel extends Model
{
    protected $table = 'news';

    // ...

    public function getPagination(?int $perPage = null): array
    {
        $this->builder()
            ->select('news.*, category.name')
            ->join('category', 'news.category_id = category.id');

        return [
            'news'  => $this->paginate($perPage),
            'pager' => $this->pager,
        ];
    }
}

Important

It is important to understand that the Model::paginate() method uses the Model and the Query Builder instance in the Model. Therefore, trying to use Model::paginate() with $db->query() will not work because $db->query() executes the query immediately and is not associated with the Query Builder.

If you need a complicated SQL query that you cannot write with Query Builder, try using $db->query() and Manual Pagination.

Paginating Multiple Results

If you need to provide links from two different result sets, you can pass group names to most of the pagination methods to keep the data separate:

<?php

namespace App\Controllers;

use App\Models\PageModel;
use App\Models\UserModel;

class UserController extends BaseController
{
    public function index()
    {
        $userModel = model(UserModel::class);
        $pageModel = model(PageModel::class);

        $data = [
            'users' => $userModel->paginate(10, 'group1'),
            'pages' => $pageModel->paginate(15, 'group2'),
            'pager' => $userModel->pager,
        ];

        echo view('users/index', $data);
    }
}
?>

<!-- In your view file: -->
<?= $pager->links('group1') ?>
<?= $pager->simpleLinks('group2') ?>

Setting Page Manually

If you need to specify which page of results to return you can specify the page as the 3rd argument. This can be handy when you have a different manner than the default $_GET variable to control which page to show.

<?php

use App\Models\UserModel;

$userModel = model(UserModel::class);
$page      = 3;

$users = $userModel->paginate(10, 'group1', $page);

Specifying the URI Segment for Page

It is also possible to use a URI segment for the page number, instead of the page query parameter. Simply specify the segment number to use as the fourth argument. URIs generated by the pager would then look like https://domain.tld/foo/bar/[pageNumber] instead of https://domain.tld/foo/bar?page=[pageNumber].

<?php

$users = $userModel->paginate(10, 'group1', null, $segment);

Please note: $segment value cannot be greater than the number of URI segments plus 1.

Manual Pagination

You may find times where you just need to create pagination based on known data. You can create links manually with the makeLinks() method, which takes the current page, the number of results per page, and the total number of items as the first, second, and third parameters, respectively:

<?php

namespace App\Controllers;

class UserController extends BaseController
{
    public function index()
    {
        // ...

        $pager = service('pager');

        $page    = (int) ($this->request->getGet('page') ?? 1);
        $perPage = 20;
        $total   = 200;

        // Call makeLinks() to make pagination links.
        $pager_links = $pager->makeLinks($page, $perPage, $total);

        $data = [
            // ...
            'pager_links' => $pager_links,
        ];

        return view('users/index', $data);
    }
}
?>

<!-- In your view file: -->
<?= $pager_links ?>

This will, by default, display the links in the normal manner, as a series of links, but you can change the display template used by passing in the name of the template as the fourth parameter. More details can be found in the following sections:

$pager->makeLinks($page, $perPage, $total, 'template_name');

It is also possible to use a URI segment for the page number, instead of the page query parameter, as described in the previous section. Specify the segment number to use as the fifth parameter to makeLinks():

$pager->makeLinks($page, $perPage, $total, 'template_name', $segment);

Please note: $segment value cannot be greater than the number of URI segments plus 1.

If you need to show many pagers on one page then the additional parameter which will define a group could be helpful:

<?php

$pager = service('pager');
$pager->setPath('path/for/my-group', 'my-group'); // Additionally you could define path for every group.
$pager->makeLinks($page, $perPage, $total, 'template_name', $segment, 'my-group');

Pagination library uses page query parameter for HTTP queries by default (if no group or default group name given) or page_[groupName] for custom group names.

Paginating with Only Expected Queries

By default, all GET queries are shown in the pagination links.

For example, when accessing the URL https://domain.tld?search=foo&order=asc&hello=i+am+here&page=2, the page 3 link can be generated, along with the other links, as follows:

<?php

echo $pager->links();
// Page 3 link: https://domain.tld?search=foo&order=asc&hello=i+am+here&page=3

The only() method allows you to limit this just to queries already expected:

<?php

echo $pager->only(['search', 'order'])->links();
// Page 3 link: https://domain.tld?search=foo&order=asc&page=3

The page query is enabled by default. And only() acts in all pagination links.