Laravel 9查询构造器如何实现按列值分组的动态分页
Laravel 9 按字段值动态分组分页实现方案
Laravel 内置的 paginate() 方法仅支持固定每页条数的偏移分页,无法直接实现「单页展示同一字段值全部对应记录」的需求,可通过自定义逻辑结合框架自带分页类实现,完全兼容原有分页渲染、接口返回的交互逻辑。
核心实现思路
不再以固定条数划分页码,而是以fruit_type的去重排序结果作为分页索引:第N页对应排序后第N个水果类型的全部关联记录,每页条数随当前类型的记录总数动态变化。
分步实现
1. 定义基础查询,复用查询条件
把通用的筛选条件抽成基础查询对象,后续不同查询场景通过clone复用,避免重复编写条件:
// 替换为实际业务的筛选条件 $baseQuery = DB::table('Fruit')->where('something', '=', 'something');
2. 获取分页索引与基础统计
从请求中读取当前页码,同时取出所有符合条件的去重水果类型,按排序规则生成页码与类型的映射关系:
// 获取当前请求页码,默认第1页 $currentPage = request()->get('page', 1); // 取所有符合条件的去重水果类型,按需求排序(可自定义排序规则,如按记录数倒序、按类型名排序等) $fruitTypes = (clone $baseQuery) ->select('fruit_type') ->distinct() ->orderBy('fruit_type') ->pluck('fruit_type'); $totalPages = $fruitTypes->count(); $totalRecords = (clone $baseQuery)->count();
3. 查询当前页数据
做页码越界判断后,取出当前页对应的水果类型,查询该类型下的全部记录:
$currentPageData = collect(); // 页码合法时查询对应数据 if ($currentPage >= 1 && $currentPage <= $totalPages) { // 数组索引从0开始,当前页对应索引为页码减1 $currentFruitType = $fruitTypes[$currentPage - 1]; $currentPageData = (clone $baseQuery) ->where('fruit_type', $currentFruitType) ->orderBy('id') // 可自定义单页内数据的排序规则 ->get(); }
4. 生成分页实例
调用Laravel内置的LengthAwarePaginator类生成分页对象,和原生paginate()返回的实例用法完全一致,支持直接返回JSON、调用links()方法渲染分页导航:
use Illuminate\Pagination\LengthAwarePaginator; $paginatedResult = new LengthAwarePaginator( $currentPageData, // 当前页数据集合 $totalRecords, // 符合条件的总记录数 $currentPageData->count(), // 当前页实际条数(动态值) $currentPage, // 当前页码 [ 'path' => request()->url(), // 分页链接基础路径 'query' => request()->query(), // 保留请求中已有的其他筛选参数 ] );
完整代码示例
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\DB; class FruitController extends Controller { public function index(Request $request) { $currentPage = $request->get('page', 1); // 基础查询,可自行扩展筛选条件 $baseQuery = DB::table('Fruit') ->where('something', '=', 'something'); // 生成分页类型索引 $fruitTypes = (clone $baseQuery) ->select('fruit_type') ->distinct() ->orderBy('fruit_type') ->pluck('fruit_type'); $totalRecords = (clone $baseQuery)->count(); $currentPageData = collect(); // 查询当前页数据 if ($currentPage >= 1 && $currentPage <= $fruitTypes->count()) { $currentFruitType = $fruitTypes[$currentPage - 1]; $currentPageData = (clone $baseQuery) ->where('fruit_type', $currentFruitType) ->orderBy('id') ->get(); } // 生成分页对象 $fruits = new LengthAwarePaginator( $currentPageData, $totalRecords, $currentPageData->count(), $currentPage, [ 'path' => $request->url(), 'query' => $request->query(), ] ); // 接口可直接return $fruits,视图场景可传参到blade后用$fruits->links()渲染分页 return view('fruit.index', compact('fruits')); } }
优化注意事项
- 给
fruit_type字段加索引,可大幅提升distinct查询、条件查询的性能 - 如果水果类型为固定枚举值,可将类型列表做缓存,避免每次请求执行distinct查询
- 如需调整类型的排序规则(如按每个类型的记录总数倒序、按最新入库时间排序),只需修改查询
$fruitTypes时的orderBy逻辑即可 - 单页内的数据排序规则可在查询
$currentPageData时自定义,不影响分页逻辑
内容的提问来源于stack exchange,提问作者Alex Coetzee
相关产品推荐
相关产品推荐

