分页
CodeIgniter 提供了一个非常简单但灵活的分页库,它易于主题化,与模型一起工作,并且能够在一个页面上支持多个分页器。
加载库
与 CodeIgniter 中的所有服务一样,它可以通过 Config\Services
加载,但您通常不需要手动加载它。
<?php
$pager = \Config\Services::pager();
使用模型进行分页
在大多数情况下,您将使用 Pager 库来对从数据库检索的结果进行分页。当使用 模型 类时,您可以使用其内置的 paginate()
方法自动检索当前批次的结果,并设置 Pager 库,使其在您的控制器中可以使用。它甚至会通过 page=X
查询变量从当前 URL 读取它应该显示的当前页面。
为了在您的应用程序中提供分页的用户列表,您的控制器方法应该类似于以下示例:
<?php
namespace App\Controllers;
class UserController extends BaseController
{
public function index()
{
$model = new \App\Models\UserModel();
$data = [
'users' => $model->paginate(10),
'pager' => $model->pager,
];
return view('users/index', $data);
}
}
在这个例子中,我们首先创建一个新的 UserModel
实例。然后,我们填充要发送到视图的数据。第一个元素是来自数据库的结果,即 **users**,它是在正确页面检索到的,每页返回 10 个用户。必须发送到视图的第二个项目是 Pager 实例本身。为了方便起见,模型将保留它使用的实例,并将其存储在公共属性 $pager
中。因此,我们获取它并将其分配给视图中的 $pager
变量。
自定义分页查询
要在模型中自定义分页查询,您可以在 paginate()
方法之前添加 查询构建器 方法。
添加 WHERE
如果您想添加 WHERE 条件,您可以直接指定条件
// In your Controller.
$model = new \App\Models\UserModel();
$data = [
'users' => $model->where('ban', 1)->paginate(10),
'pager' => $model->pager,
];
您可以将条件移到单独的方法中
<?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.
}
}
// In your Controller.
$model = new \App\Models\UserModel();
$data = [
'users' => $model->banned()->paginate(10),
'pager' => $model->pager,
];
添加 JOIN
您可以加入另一个表
<?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,
];
}
}
重要
重要的是要理解 Model::paginate()
方法使用模型和模型中的查询构建器实例。因此,尝试将 Model::paginate()
与 $db->query() 一起使用将不起作用,因为 $db->query()
会立即执行查询,并且与查询构建器无关。
如果您需要一个使用 Query Builder 无法编写的复杂 SQL 查询,请尝试使用 $db->query() 和 手动分页。
显示分页链接
然后,我们需要在视图中告诉它在哪里显示生成的链接。
<?= $pager->links() ?>
仅此而已。Pager 类将渲染第一页和最后一页链接,以及当前页两侧超过两页的任何页面的下一页和上一页链接。
重要的是要注意,下一页和上一页的库模式不同于传统分页结果的方式。
这里的下一页和上一页链接到要在分页结构中显示的链接组,而不是链接到记录的下一页或上一页。
如果您更喜欢更简单的输出,可以使用 simpleLinks()
方法,该方法仅使用“Older”和“Newer”链接,而不是详细的分页链接。
<?= $pager->simpleLinks() ?>
在幕后,库加载一个视图文件,该文件确定链接的格式,使其易于根据您的需要进行修改。有关如何完全自定义输出的详细信息,请参见下文。
对多个结果进行分页
如果您需要从两个不同的结果集中提供链接,可以将组名称传递给大多数分页方法,以使数据保持分离。
<?php
namespace App\Controllers;
class UserController extends BaseController
{
public function index()
{
$userModel = new \App\Models\UserModel();
$pageModel = new \App\Models\PageModel();
$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') ?>
手动设置页面
如果您需要指定要返回的结果的页面,可以将页面作为第三个参数指定。当您使用与默认 $_GET
变量不同的方式来控制要显示的页面时,这将非常有用。
<?php
$userModel = new \App\Models\UserModel();
$page = 3;
$users = $userModel->paginate(10, 'group1', $page);
指定页面的 URI 段
也可以使用 URI 段来表示页码,而不是页面查询参数。只需指定要使用的段号作为第四个参数即可。分页器生成的 URI 将看起来像 https://domain.tld/foo/bar/[pageNumber] 而不是 https://domain.tld/foo/bar?page=[pageNumber]。
<?php
$users = $userModel->paginate(10, 'group1', null, $segment);
请注意:$segment
值不能大于 URI 段数加 1。
手动分页
您可能会发现,有时您只需要根据已知数据创建分页。您可以使用 makeLinks()
方法手动创建链接,该方法分别将当前页、每页结果数和项目总数作为第一个、第二个和第三个参数。
<?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 ?>
默认情况下,这将以正常方式显示链接,作为一系列链接,但您可以通过传入模板名称作为第四个参数来更改使用的显示模板。更多详细信息可以在以下部分找到。
$pager->makeLinks($page, $perPage, $total, 'template_name');
也可以使用 URI 段来表示页码,而不是页码查询参数,如上一节所述。将要使用的段号指定为 makeLinks()
的第五个参数。
$pager->makeLinks($page, $perPage, $total, 'template_name', $segment);
请注意:$segment
值不能大于 URI 段数加 1。
如果您需要在一个页面上显示多个分页器,那么定义一个组的附加参数可能会有所帮助。
<?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');
分页库默认情况下使用 page 查询参数进行 HTTP 查询(如果没有给出组名或 default
组名),或者使用 page_[groupName]
来表示自定义组名。
仅使用预期查询进行分页
默认情况下,所有 GET 查询都显示在分页链接中。
例如,当访问 URL https://domain.tld?search=foo&order=asc&hello=i+am+here&page=2 时,可以生成第 3 页链接以及其他链接,如下所示。
<?php
echo $pager->links();
// Page 3 link: https://domain.tld?search=foo&order=asc&hello=i+am+here&page=3
only()
方法允许您将此限制为仅限于已预期的查询。
<?php
echo $pager->only(['search', 'order'])->links();
// Page 3 link: https://domain.tld?search=foo&order=asc&page=3
page 查询默认情况下已启用。并且 only()
在所有分页链接中起作用。
自定义链接
视图配置
当链接渲染到页面时,它们使用一个视图文件来描述 HTML。您可以通过编辑 app/Config/Pager.php 来轻松更改使用的视图。
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Pager extends BaseConfig
{
public $templates = [
'default_full' => 'CodeIgniter\Pager\Views\default_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
];
// ...
}
此设置存储别名和 命名空间视图路径,用于指定要使用的视图。 default_full
和 default_simple
视图分别用于 links()
和 simpleLinks()
方法。要更改应用程序范围内的显示方式,可以在这里分配一个新的视图。
例如,假设您创建一个新的视图文件,该文件使用 Foundation CSS 框架,并将该文件放在 app/Views/Pagers/foundation_full.php。由于 application 目录的命名空间为 App
,并且它下面的所有目录都直接映射到命名空间的段,因此您可以通过其命名空间找到视图文件。
'default_full' => 'App\Views\Pagers\foundation_full'
但是,由于它位于标准的 app/Views 目录下,因此您不需要为它命名空间,因为 view()
方法可以通过文件名找到它。在这种情况下,您只需提供子目录和文件名即可。
'default_full' => 'Pagers/foundation_full'
创建视图并在配置中设置它后,它将自动使用。您无需替换现有的模板。您可以在配置文件中创建任意数量的附加模板。常见的情况是需要为应用程序的前端和后端使用不同的样式。
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Pager extends BaseConfig
{
public $templates = [
'default_full' => 'CodeIgniter\Pager\Views\default_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'front_full' => 'App\Views\Pagers\foundation_full',
];
// ...
}
配置完成后,您可以将其指定为 links()
、simpleLinks()
和 makeLinks()
方法的最后一个参数。
<?= $pager->links('group1', 'front_full') ?>
<?= $pager->simpleLinks('group2', 'front_full') ?>
<?= $pager->makeLinks($page, $perPage, $total, 'front_full') ?>
创建视图
创建新视图时,您只需要创建用于创建分页链接本身的代码。您不应该创建不必要的包装 div,因为它可能在多个地方使用,并且您只会限制它们的用途。通过向您展示现有的 default_full
模板,最容易演示创建新视图。
<?php $pager->setSurroundCount(2) ?>
<nav aria-label="Page navigation">
<ul class="pagination">
<?php if ($pager->hasPrevious()) : ?>
<li>
<a href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>">
<span aria-hidden="true"><?= lang('Pager.first') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getPrevious() ?>" aria-label="<?= lang('Pager.previous') ?>">
<span aria-hidden="true"><?= lang('Pager.previous') ?></span>
</a>
</li>
<?php endif ?>
<?php foreach ($pager->links() as $link): ?>
<li <?= $link['active'] ? 'class="active"' : '' ?>>
<a href="<?= $link['uri'] ?>">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNext()) : ?>
<li>
<a href="<?= $pager->getNext() ?>" aria-label="<?= lang('Pager.next') ?>">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>">
<span aria-hidden="true"><?= lang('Pager.last') ?></span>
</a>
</li>
<?php endif ?>
</ul>
</nav>
setSurroundCount()
在第一行,setSurroundCount()
方法指定我们想要在当前页面链接的两侧显示两个链接。它接受的唯一参数是要显示的链接数量。
注意
您必须首先调用此方法才能生成正确的分页链接。
hasPrevious() & hasNext()
这些方法返回一个布尔值 true
,如果在当前页面两侧可以显示更多链接,则基于传递给 setSurroundCount() 的值。
例如,假设我们有 20 页数据。当前页面是第 3 页。如果周围计数为 2,则以下链接将显示如下
1 | 2 | 3 | 4 | 5
由于显示的第一个链接是第一页,因此 hasPrevious()
将返回 false
,因为没有第 0 页。但是,hasNext()
将返回 true
,因为在第 5 页之后还有 15 页结果。
getPrevious() & getNext()
这些方法返回编号链接两侧的先前或下一页结果的 **URL**。
例如,您将当前页面设置为 5,并且希望前后(周围计数)的链接分别为 2,这将为您提供类似以下内容
3 | 4 | 5 | 6 | 7
getPrevious()
返回第 2 页的 URL。 getNext()
返回第 8 页的 URL。
如果您想获取第 4 页和第 6 页,请使用 getPreviousPage() & getNextPage()。
getFirst() & getLast()
与 getPrevious() & getNext() 类似,这些方法返回结果集中第一页和最后一页的 **URL**。
links()
返回一个包含所有编号链接数据的数组。每个链接的数组包含链接的 uri、标题(只是数字)和一个布尔值,表示该链接是否为当前/活动链接。
<?php
$link = [
'active' => false,
'uri' => 'https://example.com/foo?page=2',
'title' => 1,
];
在为标准分页结构提供的代码中,方法 getPrevious() & getNext() 用于分别获取到前一个和下一个分页组的链接。
如果您想使用分页结构,其中 prev 和 next 将是基于当前页面的前一页和下一页的链接,只需将 getPrevious() & getNext() 方法替换为 getPreviousPage() & getNextPage(),并将方法 hasPrevious() & hasNext() 分别替换为 hasPreviousPage() & hasNextPage()。
以下是一个包含这些更改的示例
<nav aria-label="<?= lang('Pager.pageNavigation') ?>">
<ul class="pagination">
<?php if ($pager->hasPreviousPage()) : ?>
<li>
<a href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>">
<span aria-hidden="true"><?= lang('Pager.first') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getPreviousPage() ?>" aria-label="<?= lang('Pager.previous') ?>">
<span aria-hidden="true"><?= lang('Pager.previous') ?></span>
</a>
</li>
<?php endif ?>
<?php foreach ($pager->links() as $link): ?>
<li <?= $link['active'] ? 'class="active"' : '' ?>>
<a href="<?= $link['uri'] ?>">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNextPage()) : ?>
<li>
<a href="<?= $pager->getNextPage() ?>" aria-label="<?= lang('Pager.next') ?>">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>">
<span aria-hidden="true"><?= lang('Pager.last') ?></span>
</a>
</li>
<?php endif ?>
</ul>
</nav>
hasPreviousPage() & hasNextPage()
此方法返回一个布尔值 true
,如果分别存在指向当前显示页面的前一页和后一页的链接。
例如,假设我们有 20 页数据。当前页面是第 3 页。如果周围计数为 2,则以下链接将显示如下
1 | 2 | 3 | 4 | 5
hasPreviousPage()
将返回 true
,因为存在第 2 页。并且,hasNextPage()
将返回 true
,因为存在第 4 页。
注意
与 hasPrevious() & hasNext() 的区别在于,它们基于当前页面,而 hasPrevious() & hasNext() 基于在 setSurroundCount() 中传递的值的基础上,在当前页面之前和之后显示的链接集。
getPreviousPage() & getNextPage()
这些方法返回与当前显示页面相关的上一页和下一页的 **URL**。
例如,您将当前页面设置为 5,并且希望前后(周围计数)的链接分别为 2,这将为您提供类似以下内容
3 | 4 | 5 | 6 | 7
getPreviousPage()
返回第 4 页的 URL。 getNextPage()
返回第 6 页的 URL。
注意
getPrevious() & getNext() 返回编号链接两侧结果的上一页或下一页的 URL。
如果您想要页码而不是 URL,可以使用以下方法
getPreviousPageNumber() & getNextPageNumber()
这些方法返回与当前显示页面相关的上一页或下一页的页码。
getFirstPageNumber() & getLastPageNumber()
这些方法返回要显示的链接集中第一页和最后一页的页码。例如,如果要显示的链接集类似于以下内容
3 | 4 | 5 | 6 | 7
getFirstPageNumber()
将返回 3,而 getLastPageNumber()
将返回 7。
注意
要获取整个结果集中第一页和最后一页的页码,可以使用以下方法:第一页码始终为 1,并且可以使用 getPageCount() 获取最后一页码。
getCurrentPageNumber()
此方法返回当前页的页码。
getPageCount()
此方法返回总页数。