You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:36:27