Dcat admin使用Laravel Excel导出数据

前言

Dcat admin提供了Easy Excel导出数据(默认未安装),文档给出的例子为:

$grid->export()->rows(function (array $rows) {
    foreach ($rows as $index => &$row) {
        $row['name'] = $row['first_name'].' '.$row['last_name'];
    }

    return $rows;
});

很一目了然,也很简单,但是假如遇到很复杂的数据处理,就显得不够优雅。因此需要把数据导出提出来,做成单独的组件,在控制器中引用就可以。例如定义了一个UserExporter,那么在控制器中使用应该为:

$grid->export(new UserExporter());

在查看了Easy Excel文档后发现,支持的比较少,文档也相对模糊一些,因此决定使用Laravel Excel,结合框架本身,实现简单的使用。

开始

1、安装larave-excel

composer require maatwebsite/excel

注意laravel版本号,目前支持5.8-8以及以上的版本,默认安装的是3.1版本。

2、发布配置

php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config

生成默认配置类——config/excel.php,一般来说不需要更改配置。

使用

1、新建导出类:

例如我导出有关设备的信息,新建导出设备类DeviceExporter.php,设备表有id、name、user_id、created_at、updated_at字段:

<?php

namespace App\Admin\Extensions\Exporter;

use Dcat\Admin\Grid\Exporters\AbstractExporter;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;

class DeviceExporter extends AbstractExporter implements WithMapping, WithHeadings, FromCollection
{
    use Exportable;
    protected $fileName = '表格导出测试';
    protected $titles = [];

    public function __construct()
    {
        $this->fileName = $this->fileName.'_'.Str::random(6).'.xlsx';//拼接下载文件名称
        $this->titles = ['id' => 'id', 'user_id' => '所属用户' ,'created_at'=>'创建时间','updated_at'=>'更新时间'];
        parent::__construct();
    }

    public function export()
    {
        // TODO: Implement export() method.
        $this->download($this->fileName)->prepare(request())->send();
        exit;
    }

    public function collection()
    {
        // TODO: Implement collection() method.

        return collect($this->buildData());
    }

    public function headings(): array
    {
        // TODO: Implement headings() method.
        return $this->titles();
    }

    public function map($row): array
    {

        // TODO: Implement map() method.
        return [
            $row['id'],
            $row['user_id'],
            $row['created_at'],
            $row['updated_at'],
        ];
    }
}

可以看出,我们仅仅只需要关注maptitle即可。

2、在控制器中使用:

$grid->export(new DeviceExporter());

刷新页面就有导出按钮在页面右上角。
这里又有一个问题,之前使用Easy Excel导出数据,会有三种选项:全部、当前页和选中的项,担心使用laravel-excel会有问题,但是在AbstractExporter.php中发现这样的代码:

public function buildData(?int $page = null, ?int $perPage = null)
    {
        $model = $this->getGridModel();

        // current page
        if ($this->scope === Grid\Exporter::SCOPE_CURRENT_PAGE) {
            $page = $model->getCurrentPage();
            $perPage = $model->getPerPage();
        }

        $model->usePaginate(false);

        if ($page && $this->scope !== Grid\Exporter::SCOPE_SELECTED_ROWS) {
            $perPage = $perPage ?: $this->getChunkSize();

            $model->forPage($page, $perPage);
        }

        $array = $this->grid->processFilter()->toArray();

        $model->reset();

        return $this->normalize($this->callBuilder($array));
    }

导出依赖于数据,buildData就是在处理数据。查看SCOPE_CURRENT_PAGE

const SCOPE_ALL = 'all';
const SCOPE_CURRENT_PAGE = 'page';
const SCOPE_SELECTED_ROWS = 'selected';

刚好和我们所担心的问题一致,因此可以断定,无论使用什么导出扩展包,框架自身默认了这三种导出方式。通过获得当前页、条数等信息达到效果。并且在查看相关方法时候发现,导出方法里的导出会连同查询条件一起进行数据导出,也就是说,查询完成后导出的数据,是按照条件查询的数据。
在测试三种导出方法后,确定当前导出没有问题。

导出关联模型数据

上述的方式只能导出现有的数据,对于需要导出关联模型数据来说就不行。那能不能实现导出数据呢?可以,而且很简单。
假如我有多台设备,例如:

    |  id   | name    | user_id  | created_at  | updated_at  |
    | ----- | ------- | -------- | ----------- | ----------- |
    | 1     | 测试设备1|   2      | ----------- | ----------- |
    | 2     | 测试设备2|   0      | ----------- | ----------- |

通过在public function map($row): array{}中打印发现,如果有关联数据,打印的数据会把关联模型的数据打印出来,例如:

array:20 ["id" => 1
  "name" => "测试设备1"
  "user_id" => 2
  "created_at" => "2021-08-23 14:25:46"
  "updated_at" => "2021-08-23 14:26:10"
  "user.id" => 2
  "user.name" => "李大"
  "user.created_at" => "2021-08-13 11:21:05"
  "user.updated_at" => "2021-08-13 11:21:05"
]

而没有关联数据的则不显示:

array:20 ["id" => 2
  "name" => "测试设备2"
  "user_id" => 0
  "created_at" => "2021-08-23 14:25:46"
  "updated_at" => "2021-08-23 14:26:10"
]

所以,需要导出关联模型的数据,我们可以直接修改map代码为:

return [
    $row['id'],
    $row['user.name']??"",//为了防止无关联数据报错
    $row['created_at'],
    $row['updated_at'],
];

测试导出,发现数据无误,搞完手工。

总结

1、多个关联关系数据依然适用,还是修改map方法中的项;
2、建议复杂的数据处理还是单独写一个导出组件,这样代码层次更加清晰,数据处理也更简单;
3、至于美化Excel页面有需要的可以查看相关文档进行;
4、还有更多复杂用法,例如按照选项进行自定义导出,那个需要写好路由和查询方法,处理好导出数据即可,这部分等我实验一下再补充。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 1年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 15
TigerLin

:+1:大爱

2年前 评论

虎啊虎啊 美化excel急需

2年前 评论
bibi小将 2年前

如果数据量大如何处理呢?使用yield还是队列?

1年前 评论
她来听我的演唱会 (楼主) 1年前

请教一下大佬, Laravel Excel想使用yield和队列的话应该写在哪些位置呢,没有找到具体的循环位置 :disappointed_relieved:

1年前 评论
她来听我的演唱会 (楼主) 1年前
1syan (作者) 1年前

为啥我打印map方法中row数组 关联数据是数组形式得

file

1年前 评论

大佬我想用phpspreadsheet 进行导出,在export() 中怎么接收到map 返回的数据

1年前 评论
她来听我的演唱会 (楼主) 1年前

开启octane遇到下面问题😔

Swoole\ExitException swoole exit

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!