Dcat admin使用Laravel Excel导出数据

Laravel框架
940
0
0
2022-06-11

前言

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、还有更多复杂用法,例如按照选项进行自定义导出,那个需要写好路由和查询方法,处理好导出数据即可,这部分等我实验一下再补充。