好久没有更新博文了,很久之前做批量导入导出Excel用到了这个组件,放在腾讯文档上总觉得有点不靠谱,在这里也做个备份吧。这篇文章的大部分内容来自于组件的官方文档,不过官方并没有提供中文文档,自己花了点时间翻译的,还算有一点价值吧。

虽然说现在转行了 Java 开发,后续大概率用不到这个的。。顺便吐槽一下 Typecho 的编辑器,有那么亿点点不好用。

1. 数据类型

(1)Excel 的数据类型:
以微软的 Microsoft Office Excel 为例,支持以下几种形式的数据类型:

● string:字符串
● number:数字
● boolean:布尔值(正确/错误)
● null:空
● formula:公式
● error:错误
● Inline string / rich text:富文本,例如超链接、设置背景颜色

(2)转换到 PHP 数据类型的一些规则:
通常情况下,使用 setCellValue() 方法或者 setValue() 方法为表格赋值,PhpSpreadsheet 会自动的将 PHP 数据类型以恰当类型的填入表格,当然组件还预置了一些转换设置,可以通过修改预置的转换设置(value binder)来达到想要的转换效果。以下为部分的预置转换设置:

● 纯数字类型的字符串会被转换为数字(numbers)
● = 开头的字符串会被转换为公式(formula)
● 分数如 3/4 会被转换为小数 0.75
● 百分比如 5% 会被转换为小数 0.05
● 日期会被转换为 Excel 专有的日期时间戳格式(浮点型)
● 以科学计数法存储的字符串会被转换为数字
● \n 为该单元格内的换行符
● True 或者 False 会被转换为布尔值

(3)设置/自定义 Value Binder
前面提到了数据类型的定义问题,组件有默认使用的 DefaultValueBinder 转换,也有相对处理类型更多的 AdvancedValueBinder 转换,还有用于字符串的 StringValueBinder 转换。

// 设置要使用的类型转换器,此处使用的是 AdvancedValueBinder 转换器
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() ); 

// 接下来正常使用,和平时无区别
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$sheet->setCellValue('A4', 'Percentage value:'); //正常字符串
$sheet->setCellValue('B4', '10%'); //百分比,转换小数
$sheet->setCellValue('A5', 'Date/time value:'); //正常字符串
$sheet->setCellValue('B5', '21 December 1983'); //时间,被转换

有时候自带的类型转换器无法满足我们的需求,我们也可以自定义类型转换器。有两种自定义方法,一种是实现接口( PhpOfficePhpSpreadsheetCellIValueBinder) ,一种是继承重写原有的几个转换类( PhpOfficePhpSpreadsheetCellDefaultValueBinder 、 PhpOfficePhpSpreadsheetCellAdvancedValueBinder 、PhpOfficePhpSpreadsheetCellStringValueBinder)。以下为自定义的示例:

// 实现接口
class SelfValueBinder implements IValueBinder{
    public function bindValue(Cell $cell, $value)
    {
        // do something
    }
}

// 继承子类
class SelfValueBinder extends AdvancedValueBinder{
    public function bindValue(Cell $cell, $value)
    {
        // do something
    }
}

如果遇到长数字(如身份证号)被记为科学计数法的方式,可以尝试使用下面这个自定义 ValueBinder。

use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\DataType;

class CustomValueBinder extends AdvancedValueBinder
{
    public static function dataTypeForValue($value): string
    { //只重写dataTypeForValue方法,去掉一些不必要的判断
        if (is_null($value)) {
            return DataType::TYPE_NULL;
        } elseif ($value instanceof \PhpOffice\PhpSpreadsheet\RichText\RichText) {
            return DataType::TYPE_INLINE;
        } elseif (is_string($value) && $value[0] === '=' && strlen($value) > 1) {
            return DataType::TYPE_FORMULA;
        } elseif (is_bool($value)) {
            return DataType::TYPE_BOOL;
        } elseif (is_float($value) || is_int($value)) {
            return DataType::TYPE_NUMERIC;
        }
        return DataType::TYPE_STRING;
    }
}

2. 写入数据

写入数据后需要使用写入文件方法(IO)来保存文件,如果不需要保存在服务器上的话可以将文件输出到 PHP 缓冲区,并且指定 header 头来下载文件或者储存文件,这样子可以实现直接导出下载的效果。

(1)普通数据

use PhpOffice\PhpSpreadsheet\Spreadsheet;

$spreadsheet = new Spreadsheet(); 
$sheet = $spreadsheet->getActiveSheet(); 

// 单元格赋值写入数据几种方法:
// 一:通过 setCellValue 方法同时指定坐标及内容
$sheet->setCellValue('A1', 'Hello World !');
// 二:通过 getCell 和 setValue 方法分别指定坐标以及内容
$sheet->getCell('B8')->setValue('Some value');
// 三:指定分别行、列设置单元格的内容
$sheet->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet');
    //这会将值写入第一列、第五行,也就是A5中

(2)日期
正常的时间戳为整数,而 Excel 采用了特有的浮点型时间戳。因此写入时间需要做一下转换。组件内置了 Date::PHPToExcel 方法提供时间戳的转换。

$spreadsheet = new Spreadsheet(); 

// 获得当前时间戳
$dateTimeNow = time(); 
// 将当前时间戳转换为Excel使用的时间戳(浮点型)
$excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($dateTimeNow ); 
// 写入数据
$spreadsheet->getActiveSheet()
            ->setCellValue('A6',$excelDateValue);
// 设置单元格类型为日期
$spreadsheet->getActiveSheet()
             ->getStyle('A6')     
             ->getNumberFormat()     
             ->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME);

(3)储存0开头的数字
有两种方法:一是将单元格格式设置为字符;二是规定单元格的格式及长度,若长度不满则自动做0填充。

$spreadsheet = new Spreadsheet(); 

// 一.将单元格格式设置为字符串
$spreadsheet->getActiveSheet()
            ->setCellValueExplicit('A8',"01513789642",\PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING );

// 二.规定单元格的格式
$spreadsheet->getActiveSheet()
            ->getStyle('A9')
            ->getNumberFormat()
            ->setFormatCode('00000000000');
$spreadsheet->getActiveSheet()
            ->setCellValue('A9', 1513789642);
// 则储存的数据为 01513789642

设置单元格格式还可以做到另一个应用:带特殊符号

$spreadsheet = new Spreadsheet(); 

$spreadsheet->getActiveSheet()
            ->getStyle('A10')
            ->getNumberFormat()
            ->setFormatCode('0000-000-0000');
$spreadsheet->getActiveSheet()
            ->setCellValue('A9', 1513789642);
// 则储存的的数据为 0151-378-9642

(4)储存 PHP 数组
如果传递了一个一维的 PHP 数组进来时,数据会被以行的形式来储存,传递一个二维的 PHP 数组,数据会被看作为行列,这在导出数据库数据时可以使用,把 表头 + 数据内容 拼接成一个数组即可。具体可以看代码+图。

$spreadsheet = new Spreadsheet(); 

// 一维 PHP 数组
$rowArray = ['Value1', 'Value2', 'Value3', 'Value4'];
  // $columnArray = array_chunk($rowArray, 1);
  // 如果需要让数据竖着填充,可以用上面的方法重新分割一下数组
$spreadsheet->getActiveSheet()
            ->fromArray($rowArray,NULL,'C3');

// 二维 PHP 数组
$arrayData = [     
    [1234,2010,2011,2012],
    ['Q1',12,15,21],
    ['Q2',56,73,86],
    ['Q3',52,61,69],
    ['Q4',30,32,0], 
]; 
$spreadsheet->getActiveSheet()     
            ->fromArray($arrayData, // PHP 数组    
                        1234, // 被忽略的值,原本应为该值的位置为空
                        'C3'  // 开始存放的位置,默认为A1
                        );

请输入图片描述

图片

3.读取数据

读取数据需要使用读取文件方法(IO)载入整个文件,否则读取数据方法无法拿到数据。同时应注意校验文件的安全性,避免出现安全问题。
(1) 读取普通数据

$spreadsheet = new Spreadsheet(); 

// 按 Excel 坐标检索获取单元格的数据
$spreadsheet->getActiveSheet()
            ->getCell('A1')
            ->getValue();
            // 此处直接获取 Excel 坐标 A1 的值
            
// 按行列坐标检索获取单元格的数据  
$spreadsheet->getActiveSheet()
            ->getCellByColumnAndRow(1, 5)
            ->getValue();
            // 此处先指定坐标(1,5),对应着 A5 单元格

(2) 得到公式计算后的数据

$spreadsheet = new Spreadsheet(); 

// 得到公式计算后的数据
$spreadsheet->getActiveSheet()
            ->getCell('A4')
            ->getCalculatedValue();
            // 此处直接获取 Excel 坐标 A4 中公式计算的值

// 另一种写法,通过坐标(1,4)对应着 A4 单元格的值
$spreadsheet->getActiveSheet()
            ->getCellByColumnAndRow(1, 4)
            ->getCalculatedValue();    

(3) 公式的处理、缓存

$spreadsheet = new Spreadsheet(); 

// 得到公式、公式的处理
$spreadsheet->getActiveSheet()
            ->setCellValue(
            'A4',
            '=IF(A3, 
                CONCATENATE(A1, " ", A2), 
                CONCATENATE(A2, " ", A1))'
            );

//将该单元格标记为文本
$spreadsheet->getActiveSheet()
            ->getCell('A4')  //也可以使用getCellByColumnAndRow
            ->getStyle()
            ->setQuotePrefix(true);

可以使用 getCalculatedValue 方法来获得公式计算后的结果。如果想储存一些以 = 开头的数据,则需要将该单元格标记为文本的类型,只要将该单元格标记为文本后即使使用 getCalculatedValue 方法求值也只会返回表达式,而不是求值结果。
因为公式的计算可能会占用大量的硬件资源和时间,在读取表格公式计算值之后如果没有释放资源,公式计算结果是会被缓存的,可以设置在本次读取中禁用缓存或者清除缓存。

// 禁用公式计算结果缓存
Calculation::getInstance($spreadsheet)
            ->disableCalculationCache();

// 清除公式计算结果缓存
Calculation::getInstance($spreadsheet)
            ->clearCalculationCache();
            
// 写入时公式预计算,默认开启
// 表格较大的情况下可以关闭以提升速度
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->setPreCalculateFormulas(false);
$writer->save("test.xlsx");

(4) 返回格式化后的值(日期时间等)

$spreadsheet = new Spreadsheet(); 

$spreadsheet->getActiveSheet()
            ->getCell('A6')
            ->getFormattedValue();

(5) 返回 PHP 数组(个人推荐这个)
数据量比较多的时候不管是写入还是读取感觉用数组更方便一些,可以不用自己去写循环,也不用处理 AA 这种列的情况。返回的数组如果没做统一格式化数组键是 [0,0] 这种情况,使用了统一格式化数组键就变成 [A,1] 这样子。

$spreadsheet = new Spreadsheet(); 

// 返回指定范围内容为二维数组(行、列)
$spreadsheet->getActiveSheet()
            ->rangeToArray(
            'C3:E5', // 从C3到E5的范围     
            NULL, // 如果读取到NULL则该单元格置空    
            TRUE, // 是否计算公式,TRUE返回值,FALSE返回公式本身    
            TRUE, // 是否对单元格进行统一的格式化
            TRUE  // 是否按照行、列来索引单元格
            );
    // rangeToArray 只是其中一个方法,他会通过范围返回数组
    // toArray 方法会返回整张工作表
    // namedRangeToArray 会通过命名范围返回数组

(6) 命名范围(named range)的设置
作用:给某个单元格或者某个范围的单元格命名,方便后续调用

// 把B1单元格命名为 TAX_RATE
$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('TAX_RATE', $worksheet, '=$B$1') ); 

// 把C3到E5的单元格命名为 PRICE
$spreadsheet->addNamedRange( new \PhpOffice\PhpSpreadsheet\NamedRange('PRICE', $worksheet, 'C3:E5') ); 

3.读取/写入文件(IO)
(1)读取文件方法
● 通用 IOFactory 类
相比于专有的读写类。IOFactory 类的优势在于通用方法判断文件类型到底是早期的 Xls 还是 后续的 Xlsx 格式,不需要再自己定义不同格式调用不同的方法。

// 一.通过 IOFactory 工厂类读取文件

// 第一种方法,需要指定格式,可设置属性(如只读)
// 指定格式运行、加载速度会比自动判断稍快
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); 
$reader->setReadDataOnly(TRUE); //设置只读属性可加快读取速度
$spreadsheet = $reader->load("test.xlsx"); //加载指定文件

// 第二种方法,自动判断格式,稍慢与指定格式
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('test.xlsx');

// 第三种方法,自动判断格式,可设置属性(如只读)
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("test.xlsx"); 
$reader->setReadDataOnly(true);
$reader->load("test.xlsx");

● 专用 Reader 类

// 二.通过专用的 Reader 类读取文件
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
    // 如果是Xls格式则使用下面这个类
    // $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
$spreadsheet = $reader->load("test.xlsx");//加载指定文件

● 读取文件时可设置的属性
1.对文件设置只读属性,可加快读取速度

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load("test.xlsx");

2.读取特定的工作表

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setLoadSheetsOnly(["Sheet 1", "Sheet 2"]);
// 此处应为工作表的表名,表名之间用逗号隔开
$spreadsheet = $reader->load("test.xlsx");

(2)读取文件内容(遍历)
● 普通遍历

// 省略了文件读取,请参考上面的实例
$sheet = $spreadsheet->getActiveSheet();

foreach ($sheet->getRowIterator() as $row) {
    $cellIterator = $row->getCellIterator();
    $cellIterator->setIterateOnlyExistingCells(FALSE);
    // 当单元格迭代器类设置为 TRUE 的时候空单元格不会被遍历
    // 该选项默认为 FALSE ,会遍历表格的所有单元格
    foreach ($cellIterator as $cell) {          
         $value = $cell->getValue();
         // use value do something
    }
}

● 基于普通二维坐标遍历

// 读取文件
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); 
$reader->setReadDataOnly(TRUE); 
$spreadsheet = $reader->load("test.xlsx");  
$sheet = $spreadsheet->getActiveSheet();

// 获取工作表中行列的最大数,并将列的字母转换为数字
$highestRow = $sheet->getHighestRow(); //举例:获取到最大为 10 行
$highestColumn = $sheet->getHighestColumn(); //举例:获取到 F 列
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);  // 举例:获取到最大字母列转换,对应着数字 5

// 开始遍历每个单元格
for ($row = 1; $row <= $highestRow; ++$row) {        
    for ($col = 1; $col <= $highestColumnIndex; ++$col) { 
        $value = $sheet->getCellByColumnAndRow($col, $row)
                       ->getValue(); //获取到单个单元格数据
            // use value do something
    }
}

● 基于 Excel 坐标遍历
如果不想进行坐标转换,也可以直接使用 Excel 坐标进行遍历。当列超过 Z 时会重新以 AA 开始计数,而 AA 又会被判断为小于 B ,所以这时候我们需要将循环条件修改一下,把 <= 改为 != 作为循环的条件判断。当然这样有可能出现初始值超过导致溢出的问题,所以不建议使用 Excel 坐标遍历。或者根据实际需求自己定义循环条件。

// 读取文件
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); 
$reader->setReadDataOnly(TRUE); 
$spreadsheet = $reader->load("test.xlsx");  

// 遍历单元格前的准备,获取最大的行、列
$sheet = $spreadsheet->getActiveSheet();
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
$highestColumn++;

// 开始遍历单元格
for ($row = 1; $row <= $highestRow; ++$row) {    
    for ($col = 'A'; $col != $highestColumn; ++$col) {
        // 这里对列循环的判定改为了不等于而不是小于等于
        // 可能还要增加对 0 列(空表格)的判断
        $value = $sheet->getCell($col . $row)
                       ->getValue(); //获取单元格的数据
        // use value do something
    }
}

(2)写入文件方法
● 通用 IOFactory 类

$spreadsheet = new Spreadsheet();

// 获取内存中的表格数据,并指定格式保存
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, "Xlsx");

// 如果需要直接输出文件不储存在服务器上,则需要在 save 前设置 HTTP header 信息
//header('Content-Type: application/vnd.ms-excel'); // 设置文件类型
//header('Content-Disposition: attachment;filename="'.'文件名.xlsx"'); // 设置文件名称及文件后缀,文件名可使用变量。
//header('Cache-Control: max-age=0'); // 设置缓存时间,此处为0不缓存

// 保存文件到指定位置
$writer->save("test.xlsx");
// 如果直接输出,那么保存位置则这么写
//$writer->save('php://output');

// 写入完成后,释放内存
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);

● 专用 Writer 类

$spreadsheet = new Spreadsheet();

// 省略表格具体数据的部分,请阅读 写入数据方法/遍历写入文件 两个部分

$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);

// 如果要直接输出文件而不保存在服务器上,要设置 header 头,并将文件放到 PHP 的缓冲区中
header('Content-Type: application/vnd.ms-excel'); // 设置文件类型
header('Content-Disposition: attachment;filename="'.'文件名.xlsx"'); // 设置文件名称及文件后缀,文件名可使用变量替代。
header('Cache-Control: max-age=0'); // 设置缓存时间,此处为0不缓存
$writer->save('php://output');

// 写入完成后,释放内存
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);

4.链式操作

链式操作可以让我们顺畅的使用一些接口,使代码简洁利于维护。同时,因为减少了方法的重复调用,还可以一定程度的提高性能,很多的框架中都存在链式操作的使用。假如不使用链式操作,那么定义文档附加信息是这样的:

// 文档创建者
$spreadsheet->getProperties()->setCreator("Maarten Balliauw");
// 上一次文档修改者
$spreadsheet->getProperties()->setLastModifiedBy("Maarten Balliauw");
// 文档标题
$spreadsheet->getProperties()->setTitle("Office 2007 XLSX Test Document");
// 文档主题
$spreadsheet->getProperties()->setSubject("Office 2007 XLSX Test Document");
// 文档描述(备注)
$spreadsheet->getProperties()->setDescription("Test document");
// 文档关键字
$spreadsheet->getProperties()->setKeywords("office 2007 openxml php");
// 文档类别
$spreadsheet->getProperties()->setCategory("Test result file");

可以看见上面的代码反复的调用了 $spreadsheet->getProperties() 方法,而使用链式操作的话,代码就可以简化成这样。

$spreadsheet->getProperties()
            ->setCreator("Maarten Balliauw")
            ->setLastModifiedBy("Maarten Balliauw")
            ->setTitle("Office 2007 XLSX Test Document")
            ->setSubject("Office 2007 XLSX Test Document")
            ->setDescription("Test document.")
            ->setKeywords("office 2007 openxml php")
            ->setCategory("Test result file");

这只是其中一个示例,把原本需要7次的调用改为了只需要1次的链式调用。虽然说链式操作并不是强制要求的,但在实际使用中如果遇到需要重复使用同一个方法的情况下都可以尝试使用链式操作来简化代码,避免方法的重复调用,这样可以方便阅读和维护、提升编程和程序的效率。

5.杂项设置
(1). 工作表

// 设置当前工作表标题
$spreadsheet->getActiveSheet()->setTitle('Hello');

// 设置文件打开后第一个展示的工作表
    // 一.根据顺序设置
$spreadsheet->setActiveSheetIndex(0);
    // 二.根据真实表名设置
$spreadsheet->setActiveSheetIndexByName('工作表表名');

(2). 时间戳转换

// PHP 转 Excel , 转换后存入
$timenow = time();
$spreadsheet->getActiveSheet()
            ->setCellValue('D1', \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($timenow));

// 从 Excel 中正常读取一般不需要转换,设置格式即可
$spreadsheet->getActiveSheet()
             ->getStyle('A1')     
             ->getNumberFormat()     
             ->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_YYYYMMDD);

(3). 超链接

// 可以为某段文字附加超链接
$spreadsheet->getActiveSheet()
            ->setCellValue('E6','百度');
$spreadsheet->getActiveSheet()
            ->getCell('E6')
            ->getHyperlink()
            ->setUrl('https://example.com');

(4). 字体

$spreadsheet->getActiveSheet()
            ->getStyle('A7:B7')
            ->getFont()
            ->setBold(true)
            ->setName('Arial')    // 字体名称
            ->setSize(10);    // 字号大小,可选

(5). 文字属性
文字颜色

$spreadsheet->getActiveSheet()
            ->getStyle('A4')
            ->getFont()
            ->getColor()
            ->
setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);

文字对齐方式

// 文字对齐方式,列出其中六个
// HORIZONTAL_CENTER 水平居中
// HORIZONTAL_LEFT   水平居左
// HORIZONTAL_RIGHT  水平居右
// VERTICAL_CENTER   竖直居中
// VERTICAL_TOP      竖直顶部
// VERTICAL_BOTTOM   竖直底部

$styleArray = [
    'alignment' => [
        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
    ],
];
$spreadsheet->getStyle('A1')->applyFromArray($styleArray);

(6). 列宽

// 设置某列的列宽为具体字符
$spreadsheet->getActiveSheet()
            ->getColumnDimension('A') // A列
            ->setWidth(30); // 30个字符
            
// 设置自动计算列宽
$spreadsheet->getActiveSheet()
            ->getColumnDimension('A') // A列
            ->setAutoSize(true); // 自动计算列宽

// 设置默认(全部列)列宽为具体字符  
$spreadsheet->getActiveSheet()
            ->getDefaultColumnDimension() // 默认列宽
            ->setWidth(12); // 12个字符
            
// 关于列宽的设置,不同的链式操作组合会产生不同的效果
// 这里只举了三个例子,实际使用可以根据需求尝试不同的组合
// 如某列自动列宽、全部统一列宽、全部列自动列宽、全部自动+某列固定等

(7). 行高

$spreadsheet->getActiveSheet()
            ->getRowDimension('10') // 指定第十行
            ->setRowHeight(15);    // 行高15
            
$spreadsheet->getActiveSheet()
            ->getDefaultRowDimension() // 默认行高
            ->setRowHeight(15); //行高15
            
// 与列宽的设置相似,同样是通过不同的链式操作组合来产生不同的效果
// 但组件并没有提供自动计算行高的函数

(8). 合并、取消合并(拆分)单元格

$spreadsheet->getActiveSheet()
            ->mergeCells('A1:B2');
            // 从A1开始到B2的单元格合并为一个单元格
            
$spreadsheet->getActiveSheet()
            ->unmergeCells('A1:B2');
            // 取消A1到B2的单元格合并

(9). 单元格背景色

$spreadsheet->getStyle('A1')
            ->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
            ->getStartColor()->setARGB('00FF00');

(10). 区域边框及边框颜色

$styleArray = [
    'borders' => [
        'outline' => [
            'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THICK,
            'color' => ['argb' => 'FFFF0000'],
            // 前两位代表透明度,后六位对应着RGB颜色
            // FF表示不透明,RGB颜色可用下面这个工具转换
            // https://www.sioe.cn/yingyong/yanse-rgb-16/
        ],
    ],
];
$spreadsheet->getStyle('B2:G8')
            ->applyFromArray($styleArray);