Laravel 8.x 导出无限级分类树需先用自关联模型查出全部节点并按 parent_id 和 sort 排序,再通过 PHP 递归构建带层级、路径、缩进的树形数组,最后导出为 Excel(PhpSpreadsheet)、JSON 或 HTML 预览页,并建议添加排序控制、深度限制与 Redis 缓存。

在 Laravel 8.x 中导出完整无限级分类树(如商品类目、文章栏目等),关键不是“直接导出数据库表”,而是递归构建树形结构 + 格式化为可导出的数据(如 Excel、CSV、JSON)。下面给出清晰、可落地的实现方案。
1. 确保模型支持父子关系(典型 Nested Set 或 Closure Table 或自关联)
无限级分类常见三种实现方式,Laravel 8 推荐使用 自关联 + 递归查询(简单直观,适合中小数据量)或 Closure Table(性能更好)。这里以最常用的「自关联」为例:
- 数据表需含
id、name、parent_id(可为 null)、sort等字段 - 模型中定义关系:
class Category extends Model
{
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
}
2. 构建完整树形数组(带层级标识)
避免 N+1 查询,用一次查出全部节点,再用 PHP 递归组装。推荐使用「扁平转树」方式,高效且可控:
- 先查出所有分类,按
parent_id分组:
$all = Category::select('id', 'name', 'parent_id', 'sort')
->orderBy('parent_id')->orderBy('sort')
->get()
->groupBy('parent_id'); - 编写递归函数生成带层级、路径、深度的树:
function buildTree($groups, $parentId = 0, $level = 0, $path = '') {
$tree = [];
if (!$groups->has($parentId)) return $tree;
foreach ($groups[$parentId] as $item) {
$currentPath = $path ? "$path-{$item->name}" : $item->name;
$tree[] = [
'id' => $item->id,
'name' => $item->name,
'parent_id' => $item->parent_id,
'level' => $level,
'path' => $currentPath,
'indent' => str_repeat('│ ', $level) . '├─ ',
];
$tree = array_merge($tree, buildTree($groups, $item->id, $level + 1, $currentPath));
}
return $tree;
}
$fullTree = buildTree($all);
3. 导出为 Excel(用 PhpSpreadsheet)
安装:composer require phpoffice/phpspreadsheet
- 将 $fullTree 转为二维表格数据(含缩进、层级、完整路径):
$rows = [];
foreach ($fullTree as $node) {
$rows[] = [
$node['id'],
$node['indent'] . $node['name'], // 可视化缩进名
$node['level'],
$node['path'],
$node['parent_id'] ?: '-',
];
} - 使用 PhpSpreadsheet 写入并下载:
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray(array_merge([['ID', '分类名', '层级', '完整路径', '父ID']], $rows), NULL, 'A1');
// 设置列宽
$sheet->getColumnDimension('B')->setWidth(40);
$sheet->getColumnDimension('D')->setWidth(60);
// 下载
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="category-tree.xlsx"');
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');
4. 补充:导出为 JSON 或带 HTML 的树状预览页
如果用于前端展示或 API,可直接返回格式化 JSON:
- 添加辅助方法到模型或服务类:
public function toTreeArray($items, $parentId = 0) {
$branch = [];
foreach ($items as $item) {
if ($item->parent_id == $parentId) {
$children = $this->toTreeArray($items, $item->id);
$branch[] = array_merge($item->toArray(), ['children' => $children]);
}
}
return $branch;
} - 控制器中调用:
$all = Category::all();
return response()->json(['tree' => $this->toTreeArray($all)]);
不复杂但容易忽略的是:导出前务必对数据做 排序控制(按 parent_id + sort) 和 层级深度限制(防无限递归)。实际项目中建议加缓存(如 Redis 存树结构),避免每次导出都查库重组。











