You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
6.1 KiB
PHP
191 lines
6.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Helper;
|
|
|
|
use App\Exception\BusinessException;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
|
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
|
use PhpOffice\PhpSpreadsheet\Style\Style;
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
|
|
|
class Exporter
|
|
{
|
|
private $styleSettingClosure;
|
|
|
|
/**
|
|
* @param object $export
|
|
* @param string|null $fileName
|
|
* @param string $writerType
|
|
* @param array $headers
|
|
*
|
|
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
|
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
|
* @return BinaryFileResponse
|
|
*/
|
|
public function download($export, string $fileName, string $writerType = null)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* @param object $export
|
|
* @param string $filePath
|
|
* @param string|null $disk
|
|
* @param string $writerType
|
|
* @param mixed $diskOptions
|
|
*
|
|
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
|
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
|
* @return bool
|
|
*/
|
|
public function store(Export $export, string $filePath, string $writerType = null)
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
|
$this->fillHeading($export, $sheet);
|
|
$this->fillData($export, $sheet);
|
|
$this->setAutoSize($export, $sheet);
|
|
$closure = $export->getStyleSetting();
|
|
if ($closure) {
|
|
$closure($export, $sheet);
|
|
}
|
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
$writer->save($filePath);
|
|
}
|
|
|
|
private function fillHeading(Export $export, Worksheet $sheet)
|
|
{
|
|
$rowIndex = 0;
|
|
$firstCellKey = null;
|
|
$lastCellKey = null;
|
|
foreach ($export->headings() as $columnIndex => $name) {
|
|
$cellKey = $this->getCellKey($rowIndex, $columnIndex);
|
|
$sheet->setCellValue($cellKey, $name);
|
|
if ($columnIndex == 0) {
|
|
$firstCellKey = $cellKey;
|
|
}
|
|
$lastCellKey = $cellKey;
|
|
}
|
|
|
|
$this->setHeadingStyle($sheet, $firstCellKey . ':' . $lastCellKey);
|
|
}
|
|
|
|
private function fillData(Export $export, Worksheet $sheet, $rowIndex = 1)
|
|
{
|
|
$mode = $export->getMode();
|
|
if ($mode == Export::MODE_QUERY) {
|
|
$rowIndex = $this->fillFromQuery($export, $sheet, $rowIndex);
|
|
} elseif ($mode == Export::MODE_ARRAY) {
|
|
$rowIndex = $this->fillFromArray($export, $sheet, $rowIndex);
|
|
} elseif ($mode == Export::MODE_COLLECTION) {
|
|
$rowIndex = $this->fillFromCollection($export, $sheet, $rowIndex);
|
|
}
|
|
|
|
$summary = $export->withSummary ? $export->summary() : null;
|
|
if (!empty($summary)) {
|
|
$rowIndex = $this->fillItems($export, $sheet, [$summary], $rowIndex);
|
|
}
|
|
}
|
|
|
|
private function setHeadingStyle(Worksheet $sheet, $cellRange)
|
|
{
|
|
$sharedStyle = new Style();
|
|
$sharedStyle->applyFromArray(
|
|
[
|
|
'fill' => [
|
|
'fillType' => Fill::FILL_SOLID,
|
|
'color' => ['argb' => 'd9d9d9'],
|
|
],
|
|
'borders' => [
|
|
'allBorders' => ['borderStyle' => Border::BORDER_THIN],
|
|
],
|
|
'font' => [
|
|
'name' => '黑体',
|
|
'bold' => true,
|
|
'size' => 12
|
|
]
|
|
]
|
|
);
|
|
$sheet->duplicateStyle($sharedStyle, $cellRange);
|
|
}
|
|
|
|
private function fillFromQuery(Export $export, Worksheet $sheet, $rowIndex)
|
|
{
|
|
$query = $export->query();
|
|
if (is_null($query)) {
|
|
throw new BusinessException('query 方法未实现');
|
|
}
|
|
$page = 1;
|
|
$limit = 1000;
|
|
$chunkCompareValue = null;
|
|
$isFirstRound = true;
|
|
do {
|
|
$tmpQuery = clone $query;
|
|
$offset = ($page-1) * $limit;
|
|
$items = null;
|
|
if ($export->chunkCompareEnable) {
|
|
$options = $export->chunkCompareOptions;
|
|
if (!$isFirstRound) {
|
|
$tmpQuery->where($options['key'], $options['symbol'], $chunkCompareValue);
|
|
}
|
|
$items = $tmpQuery->limit($limit)->get();
|
|
$lastItem = $items->last();
|
|
$chunkCompareValue = $lastItem ? $lastItem->{$options['key']} : null;
|
|
} else {
|
|
$items = $tmpQuery->offset($offset)->limit($limit)->get();
|
|
}
|
|
$items = $export->rangeQueryItems($items);
|
|
$rowIndex = $this->fillItems($export, $sheet, $items, $rowIndex);
|
|
$isFirstRound = false;
|
|
$count = count($items);
|
|
$page ++;
|
|
} while ($count == $limit);
|
|
return $rowIndex;
|
|
}
|
|
|
|
private function fillFromArray(Export $export, Worksheet $sheet, $rowIndex)
|
|
{
|
|
return $this->fillItems($export, $sheet, $export->array(), $rowIndex);
|
|
}
|
|
|
|
private function fillFromCollection(Export $export, Worksheet $sheet, $rowIndex)
|
|
{
|
|
return $this->fillItems($export, $sheet, $export->collection(), $rowIndex);
|
|
}
|
|
|
|
private function fillItems(Export $export, Worksheet $sheet, $items, $rowIndex)
|
|
{
|
|
foreach ($items as $item) {
|
|
foreach ($export->map($item) as $columnIndex => $value) {
|
|
$cellKey = $this->getCellKey($rowIndex, $columnIndex);
|
|
$sheet->setCellValue($cellKey, $value);
|
|
}
|
|
$rowIndex += 1;
|
|
}
|
|
return $rowIndex;
|
|
}
|
|
|
|
private function concatCellKey($rowKey, $columnKey)
|
|
{
|
|
return $columnKey . $rowKey;
|
|
}
|
|
|
|
private function getCellKey($rowIndex, $columnIndex)
|
|
{
|
|
return $this->concatCellKey($rowIndex + 1, Excel::getColumnKey($columnIndex));
|
|
}
|
|
|
|
private function setAutoSize(Export $export, Worksheet $sheet)
|
|
{
|
|
foreach ($export->headings() as $columnIndex => $name) {
|
|
$columnKey = Excel::getColumnKey($columnIndex);
|
|
$sheet->getColumnDimension($columnKey)->setAutoSize(true);
|
|
}
|
|
}
|
|
}
|