feat: add sheet view support to XLSX writer
This makes it possible to freeze rows and columns, in addition to providing other sheet view settings like zoom options.
This commit is contained in:
1 parent
ab973cab34
commit
b54e217799
6 files changed
+394
-2
No files matched your search
@@ -3,6 +3,7 @@
|
||||
namespace Box\Spout\Writer\Common\Entity;
|
||||
|
||||
use Box\Spout\Writer\Common\Manager\SheetManager;
|
||||
use Box\Spout\Writer\XLSX\Entity\SheetView;
|
||||
|
||||
/**
|
||||
* Class Sheet
|
||||
@@ -27,6 +28,9 @@ class Sheet
|
||||
/** @var SheetManager Sheet manager */
|
||||
private $sheetManager;
|
||||
|
||||
/** @var SheetView */
|
||||
private $sheetView;
|
||||
|
||||
/**
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $associatedWorkbookId ID of the sheet's associated workbook
|
||||
@@ -108,4 +112,31 @@ class Sheet
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SheetView|null
|
||||
*/
|
||||
public function getSheetView(): ?SheetView
|
||||
{
|
||||
return $this->sheetView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SheetView $sheetView
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetView(SheetView $sheetView)
|
||||
{
|
||||
$this->sheetView = $sheetView;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSheetView(): bool
|
||||
{
|
||||
return $this->sheetView instanceof SheetView;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,9 @@ class Worksheet
|
||||
/** @var int Index of the last written row */
|
||||
private $lastWrittenRowIndex;
|
||||
|
||||
/** @var bool */
|
||||
private $hasStarted = false;
|
||||
|
||||
/**
|
||||
* Worksheet constructor.
|
||||
*
|
||||
@@ -110,4 +113,20 @@ class Worksheet
|
||||
// sheet index is zero-based, while ID is 1-based
|
||||
return $this->externalSheet->getIndex() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasStarted(): bool
|
||||
{
|
||||
return $this->hasStarted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hasStarted
|
||||
*/
|
||||
public function setHasStarted(bool $hasStarted = true): void
|
||||
{
|
||||
$this->hasStarted = $hasStarted;
|
||||
}
|
||||
}
|
||||
@@ -131,8 +131,6 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
|
||||
$worksheetFilePath = $this->getWorksheetFilePath($sheet);
|
||||
$worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet);
|
||||
|
||||
$this->worksheetManager->startSheet($worksheet);
|
||||
|
||||
$worksheets[] = $worksheet;
|
||||
$this->workbook->setWorksheets($worksheets);
|
||||
|
||||
@@ -223,8 +221,15 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
|
||||
if ($hasReachedMaxRows) {
|
||||
// ... continue writing in a new sheet if option set
|
||||
if ($this->optionsManager->getOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY)) {
|
||||
$previousSheet = $currentWorksheet->getExternalSheet();
|
||||
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
|
||||
|
||||
// Use the same sheet view if set
|
||||
$sheet = $currentWorksheet->getExternalSheet();
|
||||
if ($previousSheet->hasSheetView()) {
|
||||
$sheet->setSheetView($previousSheet->getSheetView());
|
||||
}
|
||||
|
||||
$this->addRowToWorksheet($currentWorksheet, $row);
|
||||
} else {
|
||||
// otherwise, do nothing as the data won't be written anyways
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\XLSX\Entity;
|
||||
|
||||
use Box\Spout\Common\Exception\InvalidArgumentException;
|
||||
use Box\Spout\Reader\XLSX\Helper\CellHelper;
|
||||
|
||||
class SheetView
|
||||
{
|
||||
/** @var bool */
|
||||
protected $showFormulas = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $showGridLines = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $showRowcolHeaders = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $showZeroes = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $rightToLeft = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $tabSelected = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $showOutlineSymbols = true;
|
||||
|
||||
/** @var bool */
|
||||
protected $defaultGridColor = true;
|
||||
|
||||
/** @var string */
|
||||
protected $view = 'normal';
|
||||
|
||||
/** @var string */
|
||||
protected $topLeftCell = 'A1';
|
||||
|
||||
/** @var int */
|
||||
protected $colorId = 64;
|
||||
|
||||
/** @var int */
|
||||
protected $zoomScale = 100;
|
||||
|
||||
/** @var int */
|
||||
protected $zoomScaleNormal = 100;
|
||||
|
||||
/** @var int */
|
||||
protected $zoomScalePageLayoutView = 100;
|
||||
|
||||
/** @var int */
|
||||
protected $workbookViewId = 0;
|
||||
|
||||
/** @var int */
|
||||
protected $freezeRow = 0;
|
||||
|
||||
/** @var string */
|
||||
protected $freezeColumn = 'A';
|
||||
|
||||
/**
|
||||
* @param bool $showFormulas
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowFormulas(bool $showFormulas): self
|
||||
{
|
||||
$this->showFormulas = $showFormulas;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $showGridLines
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowGridLines(bool $showGridLines): self
|
||||
{
|
||||
$this->showGridLines = $showGridLines;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $showRowcolHeaders
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowRowcolHeaders(bool $showRowcolHeaders): self
|
||||
{
|
||||
$this->showRowcolHeaders = $showRowcolHeaders;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $showZeroes
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowZeroes(bool $showZeroes): self
|
||||
{
|
||||
$this->showZeroes = $showZeroes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $rightToLeft
|
||||
* @return $this
|
||||
*/
|
||||
public function setRightToLeft(bool $rightToLeft): self
|
||||
{
|
||||
$this->rightToLeft = $rightToLeft;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $tabSelected
|
||||
* @return $this
|
||||
*/
|
||||
public function setTabSelected(bool $tabSelected): self
|
||||
{
|
||||
$this->tabSelected = $tabSelected;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $showOutlineSymbols
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowOutlineSymbols(bool $showOutlineSymbols): self
|
||||
{
|
||||
$this->showOutlineSymbols = $showOutlineSymbols;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $defaultGridColor
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaultGridColor(bool $defaultGridColor): self
|
||||
{
|
||||
$this->defaultGridColor = $defaultGridColor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $view
|
||||
* @return $this
|
||||
*/
|
||||
public function setView(string $view): self
|
||||
{
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $topLeftCell
|
||||
* @return $this
|
||||
*/
|
||||
public function setTopLeftCell(string $topLeftCell): self
|
||||
{
|
||||
$this->topLeftCell = $topLeftCell;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $colorId
|
||||
* @return $this
|
||||
*/
|
||||
public function setColorId(int $colorId): self
|
||||
{
|
||||
$this->colorId = $colorId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $zoomScale
|
||||
* @return $this
|
||||
*/
|
||||
public function setZoomScale(int $zoomScale): self
|
||||
{
|
||||
$this->zoomScale = $zoomScale;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $zoomScaleNormal
|
||||
* @return $this
|
||||
*/
|
||||
public function setZoomScaleNormal(int $zoomScaleNormal): self
|
||||
{
|
||||
$this->zoomScaleNormal = $zoomScaleNormal;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $zoomScalePageLayoutView
|
||||
* @return $this
|
||||
*/
|
||||
public function setZoomScalePageLayoutView(int $zoomScalePageLayoutView): self
|
||||
{
|
||||
$this->zoomScalePageLayoutView = $zoomScalePageLayoutView;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $workbookViewId
|
||||
* @return $this
|
||||
*/
|
||||
public function setWorkbookViewId(int $workbookViewId): self
|
||||
{
|
||||
$this->workbookViewId = $workbookViewId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $freezeRow Set to 2 to fix the first row
|
||||
* @return $this
|
||||
*/
|
||||
public function setFreezeRow(int $freezeRow): self
|
||||
{
|
||||
if ($freezeRow < 1) {
|
||||
throw new InvalidArgumentException('Freeze row must be a positive integer', 1589543073);
|
||||
}
|
||||
|
||||
$this->freezeRow = $freezeRow;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $freezeColumn Set to B to fix the first column
|
||||
* @return $this
|
||||
*/
|
||||
public function setFreezeColumn(string $freezeColumn): self
|
||||
{
|
||||
$this->freezeColumn = strtoupper($freezeColumn);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getXml(): string
|
||||
{
|
||||
return '<sheetView' . $this->getSheetViewAttributes() . '>' .
|
||||
$this->getFreezeCellPaneXml() .
|
||||
'</sheetView>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getSheetViewAttributes(): string
|
||||
{
|
||||
// Get class properties
|
||||
$propertyValues = get_object_vars($this);
|
||||
unset($propertyValues['freezeRow'], $propertyValues['freezeColumn']);
|
||||
|
||||
return $this->generateAttributes($propertyValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getFreezeCellPaneXml(): string
|
||||
{
|
||||
if ($this->freezeRow < 2 && $this->freezeColumn === 'A') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$columnIndex = CellHelper::getColumnIndexFromCellIndex($this->freezeColumn . '1');
|
||||
|
||||
return '<pane' . $this->generateAttributes([
|
||||
'xSplit' => $columnIndex,
|
||||
'ySplit' => $this->freezeRow - 1,
|
||||
'topLeftCell' => $this->freezeColumn . $this->freezeRow,
|
||||
'activePane' => 'bottomRight',
|
||||
'state' => 'frozen',
|
||||
]) . '/>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data with key containing the attribute name and value containing the attribute value
|
||||
* @return string
|
||||
*/
|
||||
protected function generateAttributes(array $data): string
|
||||
{
|
||||
// Create attribute for each key
|
||||
$attributes = array_map(function ($key, $value) {
|
||||
if (is_bool($value)) {
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
return $key . '="' . $value . '"';
|
||||
}, array_keys($data), $data);
|
||||
|
||||
// Append all attributes
|
||||
return ' ' . implode(' ', $attributes);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use Box\Spout\Writer\Common\Helper\CellHelper;
|
||||
use Box\Spout\Writer\Common\Manager\RowManager;
|
||||
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
|
||||
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
|
||||
use Box\Spout\Writer\XLSX\Entity\SheetView;
|
||||
use Box\Spout\Writer\XLSX\Manager\Style\StyleManager;
|
||||
|
||||
/**
|
||||
@@ -107,13 +108,34 @@ EOD;
|
||||
*/
|
||||
public function startSheet(Worksheet $worksheet)
|
||||
{
|
||||
// Only start once
|
||||
if ($worksheet->hasStarted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sheetFilePointer = \fopen($worksheet->getFilePath(), 'w');
|
||||
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
|
||||
|
||||
$worksheet->setFilePointer($sheetFilePointer);
|
||||
|
||||
\fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
|
||||
$this->addSheetViews($sheetFilePointer, $worksheet);
|
||||
\fwrite($sheetFilePointer, '<sheetData>');
|
||||
|
||||
$worksheet->setHasStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $sheetFilePointer
|
||||
* @param resourceWorksheet $worksheet
|
||||
* @return void
|
||||
*/
|
||||
private function addSheetViews($sheetFilePointer, Worksheet $worksheet): void
|
||||
{
|
||||
$sheet = $worksheet->getExternalSheet();
|
||||
if ($sheet->hasSheetView()) {
|
||||
\fwrite($sheetFilePointer, '<sheetViews>' . $sheet->getSheetView()->getXml() . '</sheetViews>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,6 +157,9 @@ EOD;
|
||||
*/
|
||||
public function addRow(Worksheet $worksheet, Row $row)
|
||||
{
|
||||
// Start the sheet when the first row is added
|
||||
$this->startSheet($worksheet);
|
||||
|
||||
if (!$this->rowManager->isEmpty($row)) {
|
||||
$this->addNonEmptyRow($worksheet, $row);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user