Pagination and sorting
When there are hundreds of posts, they are shown in parts: ten per page, with links "1, 2, 3". This is called pagination. In Yii the Pagination class calculates the number of pages and the offset, and the LinkPager widget draws the links.
Pagination in a controller
// controllers/PostController.php
use yii\data\Pagination;
public function actionIndex(): string
{
$query = Post::find()->where(['status' => 1]);
$pages = new Pagination([
'totalCount' => $query->count(),
'pageSize' => 10,
]);
$posts = $query
->orderBy(['created_at' => SORT_DESC])
->offset($pages->offset)
->limit($pages->limit)
->all();
return $this->render('index', ['posts' => $posts, 'pages' => $pages]);
}Pagination receives the total number of rows and the page size, reads the page number from the page URL parameter and calculates offset and limit for the query.
Page links
In the view views/post/index.php print the widget after the list of posts:
<?php
// views/post/index.php
use yii\bootstrap5\LinkPager;
?>
<?= LinkPager::widget(['pagination' => $pages]) ?>Page links appear under the list. The second page opens at /posts?page=2. If there is only one page, the widget prints nothing.
Sorting
The Sort class lets the visitor choose the order of rows:
use yii\data\Sort;
$sort = new Sort([
'attributes' => ['title', 'created_at'],
'defaultOrder' => ['created_at' => SORT_DESC],
]);
$posts = Post::find()->orderBy($sort->orders)->all();attributes lists the columns that may be used for sorting. Pass $sort to the view and print a link: <?= $sort->link('title') ?>. A click on it opens /posts?sort=title, a second click /posts?sort=-title with the reverse order.
The data provider
ActiveDataProvider joins a query, pagination and sorting in one object:
use yii\data\ActiveDataProvider;
public function actionIndex(): string
{
$dataProvider = new ActiveDataProvider([
'query' => Post::find()->where(['status' => 1]),
'pagination' => ['pageSize' => 10],
'sort' => ['defaultOrder' => ['created_at' => SORT_DESC]],
]);
return $this->render('index', ['dataProvider' => $dataProvider]);
}You no longer count rows or set offset by hand.
| Call | What it returns |
|---|---|
$dataProvider->getModels() | The posts of the current page |
$dataProvider->getTotalCount() | The total number of posts |
$dataProvider->pagination | The Pagination object for LinkPager |
$dataProvider->sort | The Sort object |
The data provider is the basis of the ready-made tables and lists covered in the lesson on data widgets.
Common mistake
Do not call all(), limit() or offset() on the query for a data provider. It needs a query that has not run yet: it counts the rows and adds the limits itself. Passing Post::find()->all() causes an error, because that is already an array, not a query.