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

Laravel集成Datatables出现双分页器及大数据性能问题求助

Laravel + Datatables 分页冲突与大数据卡顿解决方案

解决这个问题的核心是改用Datatables的服务器端处理模式,让后端每次只返回当前页的数据,彻底避免加载全部6000条数据导致的卡顿,同时也不会出现两个分页器的冲突。下面是具体实现步骤:

一、问题根源说明

你当前的写法混合了Laravel后端分页(提前渲染部分数据)和Datatables前端分页(基于已渲染的DOM数据),导致出现两个分页器;如果去掉Laravel分页,Datatables会一次性加载所有6000条数据到DOM,引发浏览器卡顿。服务器端模式下,Datatables通过AJAX请求后端接口,后端在数据库层面完成分页、排序、过滤,只返回当前页的少量数据,从根源解决卡顿问题。

二、后端实现(控制器+路由)

1. 安装Datatables辅助包(可选但推荐)

使用Yajra的Laravel Datatables包可以大幅简化后端逻辑,执行命令安装:

composer require yajra/laravel-datatables-oracle:^9.0

(版本号需匹配你的Laravel版本,Laravel 8+对应^9.0,Laravel 9+对应^10.0)

2. 控制器新增Datatables接口方法

在你的Almacenes控制器中添加datatable方法,负责处理AJAX请求:

use Illuminate\Http\Request;
use Yajra\DataTables\Facades\DataTables;

public function datatable(Request $request)
{
    // 预加载centrocosto关联,避免N+1查询性能问题
    $almacenes = Almacene::with('centrocosto');

    return DataTables::of($almacenes)
        // 自定义activo列的显示内容
        ->addColumn('activo', function ($almacen) {
            return $almacen->activo == 1 
                ? '<span class="label label-info">SI</span>' 
                : '<span class="label label-inverse">NO</span>';
        })
        // 自定义操作按钮列
        ->addColumn('acciones', function ($almacen) {
            return '<div class="btn-group">
                        <a href="'.route('almacenes.edit', $almacen->id).'" class="btn btn-primary">Editar</a>
                        <a href="'.route('almacenes.destroy', $almacen->id).'" class="btn btn-danger">Eliminar</a>
                    </div>';
        })
        // 标记这两个列的HTML无需转义
        ->rawColumns(['activo', 'acciones'])
        ->make(true);
}

3. 添加路由

在routes/web.php中添加对应路由:

Route::get('almacenes/datatable', [AlmacenesController::class, 'datatable'])->name('almacenes.datatable');

三、前端视图修改

1. 简化表格结构

去掉Laravel的循环渲染和分页代码,tbody留空由Datatables填充:

<div class="table-responsive">
    <table id="table" class="table table-striped">
        <thead>
            <tr>
                <th>Nombre</th>
                <th>Centro de costos</th>
                <th>Descripción</th>
                <th>Activo</th>
                <th>Acciones</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
</div>

2. 配置Datatables启用服务器端模式

修改JS代码,指定AJAX地址和列映射:

$(document).ready(function () {
    $('#table').DataTable({
        serverSide: true, // 关键:启用服务器端处理
        ajax: "{{ route('almacenes.datatable') }}", // 后端接口路由
        order: [[0, 'desc']], // 默认排序规则
        columns: [
            { data: 'nombre' }, // 对应模型的nombre字段
            { data: 'centrocosto.nombre' }, // 对应关联模型的nombre字段
            { data: 'descripcion' },
            { data: 'activo' }, // 对应控制器生成的自定义列
            { data: 'acciones' } // 对应控制器生成的操作列
        ],
        // 保留Bootstrap样式的可选配置
        dom: '<"top"lf>rt<"bottom"ip><"clear">',
        responsive: true
    });
});

四、模型关联检查

确保Almacene模型已定义与Centrocosto的关联:

class Almacene extends Model {
    // ... 现有代码
    public function centrocosto()
    {
        return $this->belongsTo(Centrocosto::class);
    }
}

五、手动实现后端接口(不使用Yajra包)

如果不想依赖第三方包,可以手动处理Datatables的请求参数,示例代码如下:

public function datatable(Request $request)
{
    $draw = $request->get('draw');
    $start = $request->get('start');
    $length = $request->get('length');
    $order = $request->get('order');
    $search = $request->get('search');

    // 基础查询
    $query = Almacene::with('centrocosto');

    // 搜索过滤
    if (!empty($search['value'])) {
        $query->where(function ($q) use ($search) {
            $q->where('nombre', 'like', '%'.$search['value'].'%')
              ->orWhere('descripcion', 'like', '%'.$search['value'].'%')
              ->orWhereHas('centrocosto', function ($cq) use ($search) {
                  $cq->where('nombre', 'like', '%'.$search['value'].'%');
              });
        });
    }

    // 排序
    $columnIndex = $order[0]['column'];
    $columnName = ['nombre', 'centrocosto.nombre', 'descripcion', 'activo'][$columnIndex];
    $direction = $order[0]['dir'];
    $query->orderByRaw($columnName.' '.$direction);

    // 分页
    $totalRecords = $query->count();
    $almacenes = $query->skip($start)->take($length)->get();

    // 处理数据格式
    $data = $almacenes->map(function ($almacen) {
        return [
            'nombre' => $almacen->nombre,
            'centrocosto.nombre' => $almacen->centrocosto->nombre,
            'descripcion' => $almacen->descripcion,
            'activo' => $almacen->activo == 1 ? '<span class="label label-info">SI</span>' : '<span class="label label-inverse">NO</span>',
            'acciones' => '<div class="btn-group">
                                <a href="'.route('almacenes.edit', $almacen->id).'" class="btn btn-primary">Editar</a>
                                <a href="'.route('almacenes.destroy', $almacen->id).'" class="btn btn-danger">Eliminar</a>
                            </div>'
        ];
    });

    return response()->json([
        'draw' => $draw,
        'recordsTotal' => $totalRecords,
        'recordsFiltered' => $totalRecords,
        'data' => $data
    ]);
}

内容的提问来源于stack exchange,提问作者Solucion Ya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:55:05