faster spreadsheet exporter based on opensout and other export improvements (#5238)

This commit is contained in:
Kevin Papst
2024-12-22 18:36:47 +01:00
committed by GitHub
parent c7f0508707
commit 136104d4b0
80 changed files with 2023 additions and 1699 deletions

View File

@@ -9,85 +9,60 @@
namespace App\Export\Base;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use App\Entity\ExportableItem;
use App\Export\ExportFilename;
use App\Export\Package\SpoutSpreadsheet;
use App\Export\RendererInterface;
use App\Export\TimesheetExportInterface;
use App\Repository\Query\TimesheetQuery;
use OpenSpout\Writer\XLSX\Writer;
use Symfony\Component\HttpFoundation\Response;
class XlsxRenderer extends AbstractSpreadsheetRenderer
final class XlsxRenderer implements RendererInterface, TimesheetExportInterface
{
protected function isTotalRowSupported(): bool
use ExportTrait;
public function __construct(private readonly SpreadsheetRenderer $spreadsheetRenderer)
{
return true;
}
public function getFileExtension(): string
{
return '.xlsx';
}
/**
* @return string
*/
protected function getContentType(): string
{
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}
/**
* @param Spreadsheet $spreadsheet
* @return string
* @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
{
$filename = @tempnam(sys_get_temp_dir(), 'kimai-export-xlsx');
if (false === $filename) {
throw new \Exception('Could not open temporary file');
}
$this->applyStyles($spreadsheet);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save($filename);
return $filename;
}
protected function applyStyles(Spreadsheet $spreadsheet): void
{
// Store expensive calculations for later
$sheet = $spreadsheet->getActiveSheet();
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
// Enable auto filter for header row
$sheet->setAutoFilter('A1:' . $highestColumn . '1');
// Freeze first row and date & time columns for easier navigation
$sheet->freezePane('D2');
foreach ($sheet->getColumnIterator() as $columnName => $column) {
// We default to a reasonable auto-width decided by the client,
// sadly ->getDefaultColumnDimension() is not supported so it needs
// to be specific about what column should be auto sized.
$col = $sheet->getColumnDimension($columnName);
// If no other width is specified (which defaults to -1)
if ((int) $col->getWidth() === -1) {
$col->setAutoSize(true);
}
}
// Text inside cells should be top left
$sheet
->getStyle('A2:' . $highestColumn . $highestRow)
->getAlignment()
->setVertical(Alignment::VERTICAL_TOP)
->setHorizontal(Alignment::HORIZONTAL_LEFT);
}
public function getId(): string
{
return 'xlsx';
}
public function getTitle(): string
{
return 'xlsx';
}
/**
* @param ExportableItem[] $exportItems
*/
public function render(array $exportItems, TimesheetQuery $query): Response
{
return $this->getFileResponse(
$this->renderFile($exportItems, $query),
(new ExportFilename($query))->getFilename() . '.xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
);
}
/**
* @param ExportableItem[] $exportItems
*/
public function renderFile(array $exportItems, TimesheetQuery $query): \SplFileInfo
{
$filename = @tempnam(sys_get_temp_dir(), 'kimai-export-xlsx');
if (false === $filename) {
throw new \Exception('Could not open temporary file');
}
$spreadsheet = new SpoutSpreadsheet(new Writer());
$spreadsheet->open($filename);
$this->spreadsheetRenderer->writeSpreadsheet($spreadsheet, $exportItems, $query);
return new \SplFileInfo($filename);
}
}