【问题标题】:How to pass filtered data to Datatables on Laravel?如何将过滤后的数据传递给 Laravel 上的数据表?
【发布时间】:2017-04-28 12:46:39
【问题描述】:

在我正在开发的应用程序中,我们有两个数据源:MySQL 和 Elasticsearch。粗略地说,ES 有一个 MySQL 数据的副本,以获得更好的性能。我建立了一个直接访问 MySQL 的管理面板。这对于编写操作和检索单个项目来说是可以的。但是,当我尝试列出和过滤大量记录时,速度非常慢,导致超时。然后,我只更新列表部分以从 ES 而不是 MySQL 获取数据。

面板基于Laravel 5.3 构建,我们使用Datatables plug-in for jQuery 来显示数据。要将数据表绑定到后端,我们有Datatables package for Laravel

使用以下控制器操作,并且在不应用任何过滤器时,它可以正常工作:

public function data(Elasticsearch $elastic, Request $request)
{
    $query = [];
    $cols = [];
    if ($request->has('columns')) {
        foreach ($request->get('columns') as $column) {
            $cols[] = $column['name'];
            if (!empty($column['search']['value'])) {
                $query[] = sprintf('%s:%s', $column['name'], $column['search']['value']);
            }
        }
    }

    $from = (int) $request->get('start');
    $size = (int) $request->get('length');
    $sort = [];
    if ($request->has('order')) {
        foreach ($request->get('order') as $order) {
            $sort[] = [ $cols[$order['column']] => $order['dir'] ];
        }
    } else {
        $sort[] = [ 'name' => 'asc' ];
    }
    $args = [
        'index' => 'acme',
        'type' => 'user',
        'from' => $from ?: 0,
        'size' => $size ?: 10,
        'sort' => $sort,
    ];
    if (count($query) > 0) {
        $args['q'] = implode(' AND ', $query);
    }

    $response = $elastic->search($args);
    $data = [];
    $total = 0;

    if (!empty($response['hits'])) {
        $total = $response['hits']['total'];
        $data = array_map(function ($hit) {
            return $hit['_source'];
        }, $response['hits']['hits']);
    }

    return Datatables::of(collect($data))
        ->skipPaging()
        ->setTotalRecords($total)
        ->make(true);
}

触发表格渲染的 Javascript 如下所示:

$('#users-table').DataTable({
    processing: true,
    serverSide: true,
    orderCellsTop: true,
    ajax: '{!! route('user.data') !!}',
    columns: [
        { data: 'name', name: 'name' },
        { data: 'type', name: 'type' },
        { data: 'status', name: 'status' }
    ]
});

当我尝试按某列搜索或过滤表格时出现问题。分页停止工作。我想那是因为我在将数据集传递给数据表之前在Datatables::of() 中过滤了数据集。我知道我可以从 ES 中检索整个数据集并让 Datatables 对数据进行过滤和排序,但我最终可能会遇到时间和内存使用问题。我们有数以百万计的文档被索引。

我尝试使用空函数 (->filter(function () {})) 覆盖全局搜索,但没有成功。添加时分页中断,即使记录总数设置为很大值,也只显示一页。

【问题讨论】:

  • @AddWebSolutionPvtLtd,是的。这就是我在将空函数传递给过滤器时尝试的方法。由于我已经在 ES 中过滤了数据,因此我可能将这个函数设为空。无论如何,谢谢。

标签: laravel elasticsearch laravel-5 datatables


【解决方案1】:

当我评论 Gyrocode 的答案时,我找到了一种方法让它按我想要的方式工作。我想有更优雅的方法可以做到这一点,但这个方法有效。

由于我使用的 Datatables 库采用当前请求来对数据进行排序和过滤,因此我查看了源代码以寻找使用空请求的方法。我最终发现可以实例化传递我想要的请求的引擎。

这就是我所做的:

use Yajra\Datatables\Engines\CollectionEngine;
use Yajra\Datatables\Request as DatatablesRequest;

// ...

return value(new CollectionEngine(collect($data), new DatatablesRequest()))
    ->setTotalRecords($total)
    ->make‌​(true);

这样,lib 会按原样使用数据,而不进行任何类型的过滤或排序。

【讨论】:

    【解决方案2】:

    通过查看 Laravel DataTables 源代码,有一个未记录的overrideGlobalSearch() 方法,请参见下面的定义。最后一个参数是一个标志,它决定是否应该执行默认的全局搜索。

    /**
     * Update flags to disable global search
     *
     * @param  callable $callback
     * @param  mixed $parameters
     * @param  bool $autoFilter
     */
    public function overrideGlobalSearch(callable $callback, $parameters, $autoFilter = false)
    {
    }
    

    理论上你应该能够做到以下几点:

    $datatables = Datatables::of(collect($data))
        ->skipPaging()
        ->setTotalRecords($total);
    
    $datatables->overrideGlobalSearch(function(){ }, null);
    
    return $datatables->make(true);    
    

    您还需要重置保存列搜索值的参数,以便 DataTables 默认不执行列搜索。

    除此之外,请确保您的 $total 变量在过滤之前保存记录总数。

    【讨论】:

    • 由于overrideGlobalSearch是从filter方法调用的,我尝试使用->filter(function () {})也有类似的效果。而且,不幸的是,它不起作用。当我添加它时,分页会中断。此外,您不能将overrideGlobalSearch 添加到方法链中,因为它不会返回引擎实例。谢谢!
    • 只是为了让你知道,我已经按照你的建议尝试过,这样:$datatables = Datatables::of(collect($data))->skipPaging(); $datatables->overrideGlobalSearch(function(){ }, null); return $datatables->setTotalRecords($total)->make(true);。并得到与使用->filter() 相同的结果。顺便说一句,对不起单行代码。
    • @GustavoStraube,你说得对,不能链接overrideGlobalSearch,更正了答案。您是否阅读了有关重置列搜索请求参数以及总计数与过滤记录计数的部分?
    • 从您的回答开始,我找到了实现我想要的方法:return value(new \Yajra\Datatables\Engines\CollectionEngine(collect($data), new \Yajra\Datatables\Request()))->setTotalRecords($total)->make(true);。我直接调用CollectionEngine 构造函数并向它传递一个空请求。也许有一种更优雅的方式来做到这一点,但它正在发挥作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-22
    • 1970-01-01
    • 2019-08-13
    • 2013-08-17
    • 2021-07-09
    相关资源
    最近更新 更多