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

如何使用Laravel Excel实现带分组的数据导出?

实现Laravel Excel带分组的报表导出

1. 构建结构化分组数据

不要仅依赖SQL的GROUP BY,先获取全量明细数据,在PHP层面用集合处理成嵌套分组结构:

use Maatwebsite\Excel\Concerns\FromCollection;
use App\Models\Order;

class GroupedReportExport implements FromCollection
{
    public function collection()
    {
        // 获取带关联的原始明细数据
        $records = Order::with('product')->get();
        
        // 按目标字段分组(示例:按产品分类名称)
        $groupedData = $records->groupBy('product.category.name');
        $formattedData = [];

        foreach ($groupedData as $categoryName => $orders) {
            // 添加分组标题行
            $formattedData[] = ['category' => $categoryName, 'product' => '', 'amount' => '', 'total' => ''];
            // 添加分组内的明细行
            foreach ($orders as $order) {
                $formattedData[] = [
                    'category' => '',
                    'product' => $order->product->name,
                    'amount' => $order->quantity,
                    'total' => $order->total_price
                ];
            }
            // 添加分组汇总行
            $formattedData[] = [
                'category' => '合计',
                'product' => '',
                'amount' => $orders->sum('quantity'),
                'total' => $orders->sum('total_price')
            ];
        }

        return collect($formattedData);
    }
}

2. 设置表头与分组样式

通过WithHeadings定义表头,WithStyles处理分组行的单元格合并和样式:

use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

class GroupedReportExport implements FromCollection, WithHeadings, WithStyles
{
    // ... 上述collection方法 ...

    public function headings(): array
    {
        return ['产品分类', '产品名称', '数量', '总价'];
    }

    public function styles(Worksheet $sheet)
    {
        // 表头样式
        $sheet->getStyle('A1:D1')->applyFromArray([
            'font' => ['bold' => true],
            'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFCCCCCC']]
        ]);

        $rowCount = $sheet->getHighestRow();
        for ($row = 2; $row <= $rowCount; $row++) {
            // 分组标题行:合并单元格+高亮样式
            if (!empty($sheet->getCell("A$row")->getValue()) && empty($sheet->getCell("B$row")->getValue())) {
                $sheet->mergeCells("A$row:D$row");
                $sheet->getStyle("A$row")->applyFromArray([
                    'font' => ['bold' => true, 'size' => 12],
                    'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFE6F2FF']]
                ]);
            }
            // 分组汇总行:合并单元格+强调样式
            if ($sheet->getCell("A$row")->getValue() === '合计') {
                $sheet->mergeCells("A$row:B$row");
                $sheet->getStyle("A$row:D$row")->applyFromArray([
                    'font' => ['bold' => true],
                    'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFF9E79F']]
                ]);
            }
        }

        // 设置列宽适配内容
        $sheet->getColumnDimension('A')->setWidth(20);
        $sheet->getColumnDimension('B')->setWidth(25);
        $sheet->getColumnDimension('C')->setWidth(10);
        $sheet->getColumnDimension('D')->setWidth(15);
    }
}

3. 实现Excel原生折叠分组

如果需要Excel自带的折叠/展开功能,用WithEvents监听AfterSheet事件设置大纲分组:

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;

class GroupedReportExport implements FromCollection, WithHeadings, WithStyles, WithEvents
{
    // ... 其他方法 ...

    public function registerEvents(): array
    {
        return [
            AfterSheet::class => function(AfterSheet $event) {
                $sheet = $event->sheet->getDelegate();
                $rowCount = $sheet->getHighestRow();
                $currentGroupStart = 0;

                for ($row = 2; $row <= $rowCount; $row++) {
                    // 定位分组标题行,记录明细起始行
                    if (!empty($sheet->getCell("A$row")->getValue()) && empty($sheet->getCell("B$row")->getValue())) {
                        if ($currentGroupStart !== 0) {
                            // 为上一组的明细行设置分组,默认折叠
                            $sheet->groupRows($currentGroupStart, $row-2, true);
                        }
                        $currentGroupStart = $row+1;
                    }
                }
                // 处理最后一组
                if ($currentGroupStart !== 0 && $currentGroupStart < $rowCount) {
                    $sheet->groupRows($currentGroupStart, $rowCount-1, true);
                }
            },
        ];
    }
}

4. 控制器调用导出

和基础导出逻辑一致,直接实例化导出类即可:

use Maatwebsite\Excel\Facades\Excel;

public function exportGroupedReport()
{
    return Excel::download(new GroupedReportExport(), '分组报表.xlsx');
}

核心思路:先通过集合处理出包含分组标题、明细、汇总的完整数据集,再利用Laravel Excel的样式和事件能力,还原Excel的分组视觉效果和交互功能,避免仅靠SQL分组无法实现的复杂报表结构。

内容的提问来源于stack exchange,提问作者Diwani Hime

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:30:09