Move Sheet to Common/Entity

... and introduce a SheetManager
This commit is contained in:
Adrien Loison 2017-05-30 16:34:06 +02:00
parent 30366e6a5d
commit d5f1bf5897
14 changed files with 252 additions and 112 deletions

View File

@ -2,7 +2,7 @@
namespace Box\Spout\Writer\Common\Creator;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Common\Entity\Workbook;
use Box\Spout\Writer\Common\Entity\Worksheet;
@ -14,6 +14,19 @@ use Box\Spout\Writer\Common\Entity\Worksheet;
*/
class EntityFactory
{
/** @var ManagerFactory */
private $managerFactory;
/**
* EntityFactory constructor.
*
* @param ManagerFactory $managerFactory
*/
public function __construct(ManagerFactory $managerFactory)
{
$this->managerFactory = $managerFactory;
}
/**
* @return Workbook
*/
@ -31,4 +44,15 @@ class EntityFactory
{
return new Worksheet($worksheetFilePath, $externalSheet);
}
/**
* @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
* @return Sheet
*/
public function createSheet($sheetIndex, $associatedWorkbookId)
{
$sheetManager = $this->managerFactory->createSheetManager();
return new Sheet($sheetIndex, $associatedWorkbookId, $sheetManager);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Box\Spout\Writer\Common\Creator;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Manager\SheetManager;
/**
* Class ManagerFactory
* Factory to create managers
*
* @package Box\Spout\Writer\Common\Creator
*/
class ManagerFactory
{
/**
* @return SheetManager
*/
public function createSheetManager()
{
$stringHelper = new StringHelper();
return new SheetManager($stringHelper);
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace Box\Spout\Writer\Common\Entity;
use Box\Spout\Writer\Common\Manager\SheetManager;
/**
* Class Sheet
* External representation of a worksheet
*
* @package Box\Spout\Writer\Common\Entity
*/
class Sheet
{
const DEFAULT_SHEET_NAME_PREFIX = 'Sheet';
/** @var int Index of the sheet, based on order in the workbook (zero-based) */
private $index;
/** @var string ID of the sheet's associated workbook. Used to restrict sheet name uniqueness enforcement to a single workbook */
private $associatedWorkbookId;
/** @var string Name of the sheet */
private $name;
/** @var SheetManager Sheet manager */
private $sheetManager;
/**
* @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
* @param SheetManager $sheetManager
*/
public function __construct($sheetIndex, $associatedWorkbookId, SheetManager $sheetManager)
{
$this->index = $sheetIndex;
$this->associatedWorkbookId = $associatedWorkbookId;
$this->sheetManager = $sheetManager;
$this->sheetManager->markWorkbookIdAsUsed($associatedWorkbookId);
$this->setName(self::DEFAULT_SHEET_NAME_PREFIX . ($sheetIndex + 1));
}
/**
* @api
* @return int Index of the sheet, based on order in the workbook (zero-based)
*/
public function getIndex()
{
return $this->index;
}
/**
* @return string
*/
public function getAssociatedWorkbookId()
{
return $this->associatedWorkbookId;
}
/**
* @api
* @return string Name of the sheet
*/
public function getName()
{
return $this->name;
}
/**
* Sets the name of the sheet. Note that Excel has some restrictions on the name:
* - it should not be blank
* - it should not exceed 31 characters
* - it should not contain these characters: \ / ? * : [ or ]
* - it should be unique
*
* @api
* @param string $name Name of the sheet
* @return Sheet
* @throws \Box\Spout\Writer\Exception\InvalidSheetNameException If the sheet's name is invalid.
*/
public function setName($name)
{
$this->sheetManager->throwIfNameIsInvalid($name, $this);
$this->name = $name;
$this->sheetManager->markSheetNameAsUsed($this);
return $this;
}
}

View File

@ -2,8 +2,6 @@
namespace Box\Spout\Writer\Common\Entity;
use Box\Spout\Writer\Common\Sheet;
/**
* Class Worksheet
* Entity describing a Worksheet

View File

@ -1,20 +1,19 @@
<?php
namespace Box\Spout\Writer\Common;
namespace Box\Spout\Writer\Common\Manager;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Exception\InvalidSheetNameException;
/**
* Class Sheet
* External representation of a worksheet
* Class SheetManager
* Sheet manager
*
* @package Box\Spout\Writer\Common
* @package Box\Spout\Writer\Common\Manager
*/
class Sheet
class SheetManager
{
const DEFAULT_SHEET_NAME_PREFIX = 'Sheet';
/** Sheet name should not exceed 31 characters */
const MAX_LENGTH_SHEET_NAME = 31;
@ -22,74 +21,19 @@ class Sheet
private static $INVALID_CHARACTERS_IN_SHEET_NAME = ['\\', '/', '?', '*', ':', '[', ']'];
/** @var array Associative array [WORKBOOK_ID] => [[SHEET_INDEX] => [SHEET_NAME]] keeping track of sheets' name to enforce uniqueness per workbook */
protected static $SHEETS_NAME_USED = [];
private static $SHEETS_NAME_USED = [];
/** @var int Index of the sheet, based on order in the workbook (zero-based) */
protected $index;
/** @var string ID of the sheet's associated workbook. Used to restrict sheet name uniqueness enforcement to a single workbook */
protected $associatedWorkbookId;
/** @var string Name of the sheet */
protected $name;
/** @var \Box\Spout\Common\Helper\StringHelper */
protected $stringHelper;
/** @var StringHelper */
private $stringHelper;
/**
* @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
*/
public function __construct($sheetIndex, $associatedWorkbookId)
{
$this->index = $sheetIndex;
$this->associatedWorkbookId = $associatedWorkbookId;
if (!isset(self::$SHEETS_NAME_USED[$associatedWorkbookId])) {
self::$SHEETS_NAME_USED[$associatedWorkbookId] = [];
}
$this->stringHelper = new StringHelper();
$this->setName(self::DEFAULT_SHEET_NAME_PREFIX . ($sheetIndex + 1));
}
/**
* @api
* @return int Index of the sheet, based on order in the workbook (zero-based)
*/
public function getIndex()
{
return $this->index;
}
/**
* @api
* @return string Name of the sheet
*/
public function getName()
{
return $this->name;
}
/**
* Sets the name of the sheet. Note that Excel has some restrictions on the name:
* - it should not be blank
* - it should not exceed 31 characters
* - it should not contain these characters: \ / ? * : [ or ]
* - it should be unique
* SheetManager constructor.
*
* @api
* @param string $name Name of the sheet
* @return Sheet
* @throws \Box\Spout\Writer\Exception\InvalidSheetNameException If the sheet's name is invalid.
* @param StringHelper $stringHelper
*/
public function setName($name)
public function __construct(StringHelper $stringHelper)
{
$this->throwIfNameIsInvalid($name);
$this->name = $name;
self::$SHEETS_NAME_USED[$this->associatedWorkbookId][$this->index] = $name;
return $this;
$this->stringHelper = $stringHelper;
}
/**
@ -97,10 +41,11 @@ class Sheet
* @see Sheet::setName for validity rules.
*
* @param string $name
* @param Sheet $sheet The sheet whose future name is checked
* @return void
* @throws \Box\Spout\Writer\Exception\InvalidSheetNameException If the sheet's name is invalid.
*/
protected function throwIfNameIsInvalid($name)
public function throwIfNameIsInvalid($name, Sheet $sheet)
{
if (!is_string($name)) {
$actualType = gettype($name);
@ -111,7 +56,7 @@ class Sheet
$failedRequirements = [];
$nameLength = $this->stringHelper->getStringLength($name);
if (!$this->isNameUnique($name)) {
if (!$this->isNameUnique($name, $sheet)) {
$failedRequirements[] = 'It should be unique';
} else {
if ($nameLength === 0) {
@ -145,7 +90,7 @@ class Sheet
* @param string $name
* @return bool TRUE if the name contains invalid characters, FALSE otherwise.
*/
protected function doesContainInvalidCharacters($name)
private function doesContainInvalidCharacters($name)
{
return (str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name);
}
@ -156,7 +101,7 @@ class Sheet
* @param string $name
* @return bool TRUE if the name starts or ends with a single quote, FALSE otherwise.
*/
protected function doesStartOrEndWithSingleQuote($name)
private function doesStartOrEndWithSingleQuote($name)
{
$startsWithSingleQuote = ($this->stringHelper->getCharFirstOccurrencePosition('\'', $name) === 0);
$endsWithSingleQuote = ($this->stringHelper->getCharLastOccurrencePosition('\'', $name) === ($this->stringHelper->getStringLength($name) - 1));
@ -168,16 +113,37 @@ class Sheet
* Returns whether the given name is unique.
*
* @param string $name
* @param Sheet $sheet The sheet whose future name is checked
* @return bool TRUE if the name is unique, FALSE otherwise.
*/
protected function isNameUnique($name)
private function isNameUnique($name, Sheet $sheet)
{
foreach (self::$SHEETS_NAME_USED[$this->associatedWorkbookId] as $sheetIndex => $sheetName) {
if ($sheetIndex !== $this->index && $sheetName === $name) {
foreach (self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()] as $sheetIndex => $sheetName) {
if ($sheetIndex !== $sheet->getIndex() && $sheetName === $name) {
return false;
}
}
return true;
}
/**
* @param int $workbookId Workbook ID associated to a Sheet
* @return void
*/
public function markWorkbookIdAsUsed($workbookId)
{
if (!isset(self::$SHEETS_NAME_USED[$workbookId])) {
self::$SHEETS_NAME_USED[$workbookId] = [];
}
}
/**
* @param Sheet $sheet
* @return void
*/
public function markSheetNameAsUsed(Sheet $sheet)
{
self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()][$sheet->getIndex()] = $sheet->getName();
}
}

View File

@ -6,7 +6,7 @@ use Box\Spout\Common\Exception\IOException;
use Box\Spout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface;
use Box\Spout\Writer\Common\Entity\Options;
use Box\Spout\Writer\Common\Manager\Style\StyleManagerInterface;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Common\Entity\Workbook;
use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\Exception\SheetNotFoundException;
@ -113,7 +113,7 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
$worksheets = $this->getWorksheets();
$newSheetIndex = count($worksheets);
$sheet = new Sheet($newSheetIndex, $this->workbook->getInternalId());
$sheet = $this->entityFactory->createSheet($newSheetIndex, $this->workbook->getInternalId());
$worksheetFilePath = $this->getWorksheetFilePath($sheet);
$worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet);

View File

@ -3,7 +3,7 @@
namespace Box\Spout\Writer\Common\Manager;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Common\Entity\Workbook;
use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\Exception\SheetNotFoundException;

View File

@ -2,10 +2,9 @@
namespace Box\Spout\Writer\ODS\Manager;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Common\Manager\WorkbookManagerAbstract;
use Box\Spout\Writer\ODS\Helper\FileSystemHelper;
use Box\Spout\Writer\ODS\Helper\StyleHelper;
use Box\Spout\Writer\ODS\Manager\Style\StyleManager;
/**

View File

@ -6,6 +6,7 @@ use Box\Spout\Common\Exception\UnsupportedTypeException;
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
use Box\Spout\Common\Type;
use Box\Spout\Writer\Common\Creator\EntityFactory;
use Box\Spout\Writer\Common\Creator\ManagerFactory;
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
@ -58,7 +59,9 @@ class WriterFactory
$optionsManager = new XLSX\Manager\OptionsManager($styleBuilder);
$styleMerger = new StyleMerger();
$globalFunctionsHelper = new GlobalFunctionsHelper();
$internalFactory = new XLSX\Creator\InternalFactory(new EntityFactory());
$entityFactory = new EntityFactory(new ManagerFactory());
$internalFactory = new XLSX\Creator\InternalFactory($entityFactory);
return new XLSX\Writer($optionsManager, $styleMerger, $globalFunctionsHelper, $internalFactory);
}
@ -72,7 +75,9 @@ class WriterFactory
$optionsManager = new ODS\Manager\OptionsManager($styleBuilder);
$styleMerger = new StyleMerger();
$globalFunctionsHelper = new GlobalFunctionsHelper();
$internalFactory = new ODS\Creator\InternalFactory(new EntityFactory());
$entityFactory = new EntityFactory(new ManagerFactory());
$internalFactory = new ODS\Creator\InternalFactory($entityFactory);
return new ODS\Writer($optionsManager, $styleMerger, $globalFunctionsHelper, $internalFactory);
}

View File

@ -3,10 +3,12 @@
namespace Box\Spout\Writer;
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Entity\Options;
use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
use Box\Spout\Writer\Exception\SheetNotFoundException;
use Box\Spout\Writer\Exception\WriterNotOpenedException;
use Box\Spout\Writer\Common\Creator\InternalFactoryInterface;
use Box\Spout\Writer\Common\Manager\WorkbookManagerInterface;
@ -49,7 +51,7 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* @api
* @param bool $shouldCreateNewSheetsAutomatically Whether new sheets should be automatically created when the max rows limit per sheet is reached
* @return WriterMultiSheetsAbstract
* @throws \Box\Spout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
* @throws WriterAlreadyOpenedException If the writer was already opened
*/
public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAutomatically)
{
@ -77,8 +79,8 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* Returns all the workbook's sheets
*
* @api
* @return Common\Sheet[] All the workbook's sheets
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @return Sheet[] All the workbook's sheets
* @throws WriterNotOpenedException If the writer has not been opened yet
*/
public function getSheets()
{
@ -99,8 +101,8 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
*
* @api
* @return Common\Sheet The created sheet
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @return Sheet The created sheet
* @throws WriterNotOpenedException If the writer has not been opened yet
*/
public function addNewSheetAndMakeItCurrent()
{
@ -114,8 +116,8 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* Returns the current sheet
*
* @api
* @return Common\Sheet The current sheet
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @return Sheet The current sheet
* @throws WriterNotOpenedException If the writer has not been opened yet
*/
public function getCurrentSheet()
{
@ -128,10 +130,10 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @api
* @param Common\Sheet $sheet The sheet to set as current
* @param Sheet $sheet The sheet to set as current
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @throws \Box\Spout\Writer\Exception\SheetNotFoundException If the given sheet does not exist in the workbook
* @throws WriterNotOpenedException If the writer has not been opened yet
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet($sheet)
{
@ -143,7 +145,7 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* Checks if the workbook has been created. Throws an exception if not created yet.
*
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the workbook is not created yet
* @throws WriterNotOpenedException If the workbook is not created yet
*/
protected function throwIfWorkbookIsNotAvailable()
{
@ -161,7 +163,7 @@ abstract class WriterMultiSheetsAbstract extends WriterAbstract
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param \Box\Spout\Writer\Common\Entity\Style\Style $style Style to be applied to the row.
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
* @throws WriterNotOpenedException If the book is not created yet
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow, $style)

View File

@ -2,7 +2,7 @@
namespace Box\Spout\Writer\XLSX\Manager;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\Common\Manager\WorkbookManagerAbstract;
use Box\Spout\Writer\XLSX\Helper\FileSystemHelper;
use Box\Spout\Writer\XLSX\Manager\Style\StyleManager;

View File

@ -1,20 +1,44 @@
<?php
namespace Box\Spout\Writer\Common;
namespace Box\Spout\Writer\Common\Entity;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Manager\SheetManager;
/**
* Class SheetTest
*
* @package Box\Spout\Writer\Common
* @package Box\Spout\Writer\Common\Entity
*/
class SheetTest extends \PHPUnit_Framework_TestCase
{
/** @var SheetManager */
private $sheetManager;
/**
* @return void
*/
public function setUp()
{
$this->sheetManager = new SheetManager(new StringHelper());
}
/**
* @param int $sheetIndex
* @param int $associatedWorkbookId
* @return Sheet
*/
private function createSheet($sheetIndex, $associatedWorkbookId)
{
return new Sheet($sheetIndex, $associatedWorkbookId, $this->sheetManager);
}
/**
* @return void
*/
public function testGetSheetName()
{
$sheets = [new Sheet(0, 'workbookId1'), new Sheet(1, 'workbookId1')];
$sheets = [$this->createSheet(0, 'workbookId1'), $this->createSheet(1, 'workbookId1')];
$this->assertEquals('Sheet1', $sheets[0]->getName(), 'Invalid name for the first sheet');
$this->assertEquals('Sheet2', $sheets[1]->getName(), 'Invalid name for the second sheet');
@ -26,7 +50,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
public function testSetSheetNameShouldCreateSheetWithCustomName()
{
$customSheetName = 'CustomName';
$sheet = new Sheet(0, 'workbookId1');
$sheet = $this->createSheet(0, 'workbookId1');
$sheet->setName($customSheetName);
$this->assertEquals($customSheetName, $sheet->getName(), "The sheet name should have been changed to '$customSheetName'");
@ -63,7 +87,8 @@ class SheetTest extends \PHPUnit_Framework_TestCase
*/
public function testSetSheetNameShouldThrowOnInvalidName($customSheetName)
{
(new Sheet(0, 'workbookId1'))->setName($customSheetName);
$sheet = $this->createSheet(0, 'workbookId1');
$sheet->setName($customSheetName);
}
/**
@ -72,7 +97,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
public function testSetSheetNameShouldNotThrowWhenSettingSameNameAsCurrentOne()
{
$customSheetName = 'Sheet name';
$sheet = new Sheet(0, 'workbookId1');
$sheet = $this->createSheet(0, 'workbookId1');
$sheet->setName($customSheetName);
$sheet->setName($customSheetName);
}
@ -85,10 +110,10 @@ class SheetTest extends \PHPUnit_Framework_TestCase
{
$customSheetName = 'Sheet name';
$sheet = new Sheet(0, 'workbookId1');
$sheet = $this->createSheet(0, 'workbookId1');
$sheet->setName($customSheetName);
$sheet = new Sheet(1, 'workbookId1');
$sheet = $this->createSheet(1, 'workbookId1');
$sheet->setName($customSheetName);
}
@ -99,13 +124,13 @@ class SheetTest extends \PHPUnit_Framework_TestCase
{
$customSheetName = 'Sheet name';
$sheet = new Sheet(0, 'workbookId1');
$sheet = $this->createSheet(0, 'workbookId1');
$sheet->setName($customSheetName);
$sheet = new Sheet(0, 'workbookId2');
$sheet = $this->createSheet(0, 'workbookId2');
$sheet->setName($customSheetName);
$sheet = new Sheet(1, 'workbookId3');
$sheet = $this->createSheet(1, 'workbookId3');
$sheet->setName($customSheetName);
}
}

View File

@ -4,7 +4,7 @@ namespace Box\Spout\Writer\ODS;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\WriterFactory;
/**

View File

@ -4,7 +4,7 @@ namespace Box\Spout\Writer\XLSX;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Entity\Sheet;
use Box\Spout\Writer\WriterFactory;
/**
@ -62,6 +62,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\XLSX\Writer $writer */
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($resourcePath);
@ -85,6 +86,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\XLSX\Writer $writer */
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($resourcePath);
@ -93,6 +95,8 @@ class SheetTest extends \PHPUnit_Framework_TestCase
$writer->addRow(['xlsx--11', 'xlsx--12']);
$writer->close();
return $sheet;
}
/**