在其他框架中,分页可能会非常痛苦。我们希望 Laravel 的分页方法会让人耳目一新。Laravel 的分页器与 查询构建器 和 Eloquent ORM 集成,并提供便捷易用、零配置的数据库记录分页功能。
默认情况下,分页器生成的 HTML 与 Tailwind CSS 框架兼容;但是,也提供 Bootstrap 分页支持。
如果您正在使用 Laravel 的默认 Tailwind 分页视图并结合 Tailwind 4.x,您的应用程序的 resources/css/app.css 文件将已正确配置为 @source Laravel 的分页视图:
@import 'tailwindcss';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';有几种方法可以对项目进行分页。最简单的方法是使用 paginate 方法在 查询构建器 或 Eloquent 查询 上。paginate 方法根据用户当前查看的页面,自动处理设置查询的“限制”和“偏移量”。默认情况下,当前页面通过 HTTP 请求中 page 查询字符串参数的值来检测。该值由 Laravel 自动检测,并自动插入到分页器生成的链接中。
在此示例中,传递给 paginate 方法的唯一参数是您希望“每页”显示的项目数。在这种情况下,让我们指定我们希望每页显示 15 个项目:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show all application users.
*/
public function index(): View
{
return view('user.index', [
'users' => DB::table('users')->paginate(15)
]);
}
}paginate 方法在从数据库检索记录之前,会计算查询匹配到的记录总数。这样做是为了让分页器知道总共有多少页记录。但是,如果你不打算在应用程序的 UI 中显示总页数,那么记录计数查询就是不必要的。
因此,如果你只需要在应用的 UI 中显示简单的“下一页”和“上一页”链接,你可以使用 simplePaginate 方法来执行单个高效的查询:
$users = DB::table('users')->simplePaginate(15);您也可以对 Eloquent 查询进行分页。在这个例子中,我们将对 App\Models\User 模型进行分页,并指出我们计划每页显示 15 条记录。正如您所看到的,其语法与查询构建器结果的分页几乎相同:
use App\Models\User;
$users = User::paginate(15);当然,您可以在对查询设置其他约束后调用 paginate 方法,例如 where 子句:
$users = User::where('votes', `>`, 100)->paginate(15);您也可以使用 simplePaginate 方法来对 Eloquent 模型进行分页:
$users = User::where('votes', `>`, 100)->simplePaginate(15);同样地,您可以使用 cursorPaginate 方法来游标分页 Eloquent 模型:
$users = User::where('votes', `>`, 100)->cursorPaginate(15);有时你可能需要在你的应用程序渲染的单个屏幕上渲染两个独立的分页器。然而,如果两个分页器实例都使用 page 查询字符串参数来存储当前页面,这两个分页器将会冲突。为了解决这个冲突,你可以通过提供给 paginate、simplePaginate 和 cursorPaginate 方法的第三个参数,来传递你希望用来存储分页器当前页面的查询字符串参数的名称:
use App\Models\User;
$users = User::where('votes', `>`, 100)->paginate(
$perPage = 15, $columns = ['*'], $pageName = 'users'
);尽管 paginate 和 simplePaginate 使用 SQL "offset" 子句创建查询,游标分页通过构建比较查询中包含的有序列的值的 "where" 子句来工作,提供了 Laravel 所有分页方法中最有效的数据库性能。这种分页方法特别适合大型数据集和“无限”滚动用户界面。
与基于偏移量的分页不同, 后者在分页器生成的URL的查询字符串中包含页码, 基于游标的分页将一个"游标"字符串放置在查询字符串中. 该游标是一个编码字符串, 包含下一个分页查询应开始分页的位置以及它应分页的方向:
http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0您可以通过查询构建器提供的 cursorPaginate 方法创建一个基于光标的分页器实例。此方法返回一个 Illuminate\Pagination\CursorPaginator 实例:
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);一旦您检索到游标分页器实例,您就可以显示分页结果,就像您通常使用 paginate 和 simplePaginate 方法时那样。有关游标分页器提供的实例方法的更多信息,请查阅游标分页器实例方法文档。
[!WARNING]
您的查询必须包含一个"order by"子句,才能利用游标分页。此外,查询排序所依据的列必须属于您正在分页的表。
为了说明偏移分页和游标分页之间的区别,让我们来研究一些示例SQL查询。以下两个查询都将显示按 id 排序的 users 表的结果的“第二页”:
# Offset Pagination...
select * from users order by id asc limit 15 offset 15;
# Cursor Pagination...
select * from users where id > 15 order by id asc limit 15;游标分页查询相对于偏移分页提供了以下优势:
然而,游标分页具有以下限制:
simplePaginate,游标分页只能用于显示 "下一页" 和 "上一页" 链接并且不支持生成带有页码的链接。null值的列不受支持。order by 子句中受支持,但前提是它们被赋予别名并也添加到 select 子句中。有时您可能希望手动创建一个分页实例,并将您已有的内存中的项目数组传递给它。您可以通过创建 Illuminate\Pagination\Paginator、Illuminate\Pagination\LengthAwarePaginator 或 Illuminate\Pagination\CursorPaginator 实例来做到这一点,具体取决于您的需求。
Paginator 和 CursorPaginator 类不需要知道结果集中的项目总数;但是,正因为如此,这些类没有用于检索最后一页索引的方法。LengthAwarePaginator 接受与 Paginator 几乎相同的参数;但是,它需要知道结果集中的项目总数。
换句话说, Paginator 对应于查询构建器上的 simplePaginate 方法, CursorPaginator 对应于 cursorPaginate 方法,以及 LengthAwarePaginator 对应于 paginate 方法。
[!WARNING]
当手动创建分页器实例时,你应该手动“切分”你传递给分页器的结果数组。如果你不确定如何操作,请查阅 array_slice PHP 函数。
默认情况下,分页器生成的链接将匹配当前请求的 URI。但是,分页器的 withPath 方法允许你自定义分页器生成链接时使用的 URI。例如,如果你希望分页器生成类似 http://example.com/admin/users?page=N,你应该将 /admin/users 传递给 withPath 方法:
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->withPath('/admin/users');
// ...
});你可以使用 appends 方法将数据附加到分页链接的查询字符串中。例如,要将 sort=votes 附加到每个分页链接中,你应该对 appends 进行如下调用:
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->appends(['sort' => 'votes']);
// ...
});您可以使用 withQueryString 方法如果您希望将当前请求的所有查询字符串值附加到分页链接:
$users = User::paginate(15)->withQueryString();如果您需要将"哈希片段"附加到分页器生成的 URL 中,您可以使用 fragment 方法。例如,要将 #users 附加到每个分页链接的末尾,您应该调用 fragment 方法,如下所示:
$users = User::paginate(15)->fragment('users');调用 paginate 方法时,你将收到一个 Illuminate\Pagination\LengthAwarePaginator 的实例,而调用 simplePaginate 方法则返回一个 Illuminate\Pagination\Paginator 的实例。最后,调用 cursorPaginate 方法会返回一个 Illuminate\Pagination\CursorPaginator 的实例。
这些对象提供了几个描述结果集的方法。除了这些辅助方法之外,分页器实例还是迭代器,可以像数组一样进行循环。因此,一旦你获取了结果,你就可以显示结果并使用Blade 渲染页面链接:
<div class="container">
@foreach ($users as $user)
{{ $user->name }}
@endforeach
</div>
{{ $users->links() }}links 方法将渲染结果集中其余页面的链接。这些链接中的每一个都将已包含适当的 page 查询字符串变量。请记住,由 links 方法生成的 HTML 与 Tailwind CSS 框架兼容。
当分页器显示分页链接时,会显示当前页码以及当前页前后三页的链接。使用 onEachSide 方法,你可以控制分页器生成的链接的中间滑动窗口中,当前页的每一侧显示多少个额外的链接:
{{ $users->onEachSide(5)->links() }}Laravel 分页器类实现了 Illuminate\Contracts\Support\Jsonable 接口契约,并公开了 toJson 方法,因此将分页结果转换为 JSON 变得非常容易。您还可以通过从路由或控制器动作中返回分页器实例,将其转换为 JSON:
use App\Models\User;
Route::get('/users', function () {
return User::paginate();
});分页器返回的 JSON 将包含元信息,例如 total、current_page、last_page 等。结果记录可通过 JSON 数组中的 data 键获取。以下是从路由返回分页器实例所创建的 JSON 示例:
{
"total": 50,
"per_page": 15,
"current_page": 1,
"last_page": 4,
"current_page_url": "http://laravel.app?page=1",
"first_page_url": "http://laravel.app?page=1",
"last_page_url": "http://laravel.app?page=4",
"next_page_url": "http://laravel.app?page=2",
"prev_page_url": null,
"path": "http://laravel.app",
"from": 1,
"to": 15,
"data":[
{
// Record...
},
{
// Record...
}
]
}默认情况下,用于显示分页链接的视图与 Tailwind CSS 框架兼容。但是,如果您没有使用 Tailwind,您可以自由定义自己的视图来渲染这些链接。在分页器实例上调用 links 方法时,您可以将视图名称作为第一个参数传递给该方法:
{{ $paginator->links('view.name') }}
<!-- Passing additional data to the view... -->
{{ $paginator->links('view.name', ['foo' => 'bar']) }}然而, 自定义分页视图最简单的方式是通过将它们导出到你的 resources/views/vendor 目录 使用 vendor:publish 命令:
php artisan vendor:publish --tag=laravel-pagination此命令会将视图文件放置在你的应用程序的 resources/views/vendor/pagination 目录中。该目录中的 tailwind.blade.php 文件对应于默认的分页视图。你可以编辑此文件来修改分页的 HTML。
如果您想指定另一个文件作为默认的分页视图,您可以在您的 App\Providers\AppServiceProvider 类的 boot 方法中调用分页器的 defaultView 和 defaultSimpleView 方法:
<?php
namespace App\Providers;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::defaultView('view-name');
Paginator::defaultSimpleView('view-name');
}
}Laravel 包含使用 Bootstrap CSS 构建的分页视图。要使用这些视图而不是默认的 Tailwind 视图,你可以在 App\Providers\AppServiceProvider 类的 boot 方法中调用分页器的 useBootstrapFour 或 useBootstrapFive 方法:
use Illuminate\Pagination\Paginator;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::useBootstrapFive();
Paginator::useBootstrapFour();
}每个分页器实例通过以下方法提供额外分页信息:
每个游标分页器实例都通过以下方法提供额外分页信息: