Framework for cell meta-data, implemented hyperlink support on it (for core formats - XLSX, HTM)

This commit is contained in:
Chris Graham 2015-03-24 14:44:54 +00:00
parent 86d78dbc09
commit b864f42a61
9 changed files with 84 additions and 16 deletions

View File

@ -38,13 +38,14 @@ abstract class AbstractWriter implements WriterInterface
abstract protected function openWriter();
/**
* Adds data to the currently openned writer.
* Adds data to the currently opened writer.
*
* @param array $dataRow Array containing data to be streamed.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
*/
abstract protected function addRowToWriter(array $dataRow);
abstract protected function addRowToWriter(array $dataRow, array $metaData);
/**
* Closes the streamer, preventing any additional writing.
@ -138,15 +139,16 @@ abstract class AbstractWriter implements WriterInterface
*
* @param array $dataRow Array containing data to be streamed.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
*
* @return \Box\Spout\Writer\AbstractWriter
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
public function addRow(array $dataRow)
public function addRow(array $dataRow, array $metaData = array())
{
if ($this->isWriterOpened) {
$this->addRowToWriter($dataRow);
$this->addRowToWriter($dataRow, $metaData);
} else {
throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
}

View File

@ -68,10 +68,11 @@ class CSV extends AbstractWriter
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow)
protected function addRowToWriter(array $dataRow, array $metaData)
{
$wasWriteSuccessful = fputcsv($this->filePointer, $dataRow, $this->fieldDelimiter, $this->fieldEnclosure);
if ($wasWriteSuccessful === false) {

View File

@ -41,10 +41,11 @@ class HTM extends AbstractWriter
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow)
protected function addRowToWriter(array $dataRow, array $metaData)
{
$wasWriteSuccessful = true;
if ($this->lastWrittenRowIndex == 0) {
@ -54,9 +55,14 @@ class HTM extends AbstractWriter
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "<tbody>\n");
}
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "<tr>\n");
foreach ($dataRow as $cell) {
foreach ($dataRow as $i => $cell) {
$cell = nl2br(htmlentities($cell));
if (isset($metaData[$i]['url']))
{
$cell = '<a href="' . htmlentities($metaData[$i]['url']) . '">' . $cell . '</a>';
}
if ($this->lastWrittenRowIndex == 0) {
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "\t<th>{$cell}</th>\n");
} else

View File

@ -208,6 +208,7 @@ EOD;
$this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
$this->createXlRelsFolder();
$this->createXlWorksheetsFolder();
$this->createXlWorksheetsRelsFolder();
return $this;
}
@ -236,6 +237,18 @@ EOD;
return $this;
}
/**
* Creates the "_rels" folder under the "worksheets" folder
*
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
*/
protected function createXlWorksheetsRelsFolder()
{
$this->createFolder($this->xlWorksheetsFolder, self::RELS_FOLDER_NAME);
return $this;
}
/**
* Creates the "styles.xml" file under the "xl" folder
*

View File

@ -165,11 +165,12 @@ class Workbook
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
* @throws \Box\Spout\Common\Exception\IOException If trying to create a new sheet and unable to open the sheet for writing
* @throws \Box\Spout\Writer\Exception\WriterException If unable to write data
*/
public function addRowToCurrentWorksheet($dataRow)
public function addRowToCurrentWorksheet($dataRow, $metaData)
{
$currentWorksheet = $this->getCurrentWorksheet();
$hasReachedMaxRows = $this->hasCurrentWorkseetReachedMaxRows();
@ -179,12 +180,12 @@ class Workbook
// ... continue writing in a new sheet if option set
if ($this->shouldCreateNewSheetsAutomatically) {
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
$currentWorksheet->addRow($dataRow);
$currentWorksheet->addRow($dataRow, $metaData);
} else {
// otherwise, do nothing as the data won't be read anyways
}
} else {
$currentWorksheet->addRow($dataRow);
$currentWorksheet->addRow($dataRow, $metaData);
}
}

View File

@ -25,6 +25,9 @@ EOD;
/** @var string Path to the XML file that will contain the sheet data */
protected $worksheetFilePath;
/** @var string Path to the XML file that will contain the sheet rels data */
protected $worksheetRelsFilePath;
/** @var \Box\Spout\Writer\Helper\XLSX\SharedStringsHelper Helper to write shared strings */
protected $sharedStringsHelper;
@ -40,6 +43,9 @@ EOD;
/** @var int */
protected $lastWrittenRowIndex = 0;
/** @var array */
protected $urls = array();
/**
* @param \Box\Spout\Writer\Sheet $externalSheet The associated "external" sheet
* @param string $tempFolder Temporary folder where the files to create the XLSX will be stored
@ -55,6 +61,7 @@ EOD;
$this->stringsEscaper = new \Box\Spout\Common\Escaper\XLSX();
$this->worksheetFilePath = $worksheetFilesFolder . DIRECTORY_SEPARATOR . strtolower($this->externalSheet->getName()) . '.xml';
$this->worksheetRelsFilePath = $worksheetFilesFolder . DIRECTORY_SEPARATOR . '_rels' . DIRECTORY_SEPARATOR . strtolower($this->externalSheet->getName()) . '.xml.rels';
$this->startSheet();
}
@ -116,10 +123,11 @@ EOD;
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the data cannot be written
*/
public function addRow($dataRow)
public function addRow($dataRow, array $metaData = array())
{
if (count($dataRow) == 0) {
// Without this fix, we get a repair issue in regular Microsoft Excel
@ -134,7 +142,9 @@ EOD;
foreach($dataRow as $cellValue) {
$columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
$data .= ' <c' . (($rowIndex == 1) ? ' s="1"' : '') . ' r="' . $columnIndex . $rowIndex . '"';
$cellPath = $columnIndex . $rowIndex;
$data .= ' <c' . (($rowIndex == 1) ? ' s="1"' : '') . ' r="' . $cellPath . '"';
if (empty($cellValue)) {
$data .= '/>' . PHP_EOL;
@ -151,6 +161,10 @@ EOD;
}
}
if (isset($metaData[$cellNumber]['url'])) {
$this->urls[$cellPath] = $metaData[$cellNumber]['url'];
}
$cellNumber++;
}
@ -173,6 +187,34 @@ EOD;
public function close()
{
fwrite($this->sheetFilePointer, ' </sheetData>' . PHP_EOL);
// Write out any hyperlinks
if (count($this->urls) != 0) {
fwrite($this->sheetFilePointer, ' <hyperlinks>' . PHP_EOL);
$i = 0;
foreach ($this->urls as $cellPath => $url) {
$refID = 'rId' . ($i + 1);
fwrite($this->sheetFilePointer, ' <hyperlink ref="' . $cellPath . '" r:id="' . $refID . '"/>' . PHP_EOL);
$i++;
}
fwrite($this->sheetFilePointer, ' </hyperlinks>' . PHP_EOL);
}
// Write rels file
$sheetRelsFilePointer = fopen($this->worksheetRelsFilePath, 'w');
if (!$sheetRelsFilePointer) throw new IOException('Unable to open rels sheet for writing.');
fwrite($sheetRelsFilePointer, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . PHP_EOL);
fwrite($sheetRelsFilePointer, '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' . PHP_EOL);
$i = 0;
foreach ($this->urls as $url) {
$refID = 'rId' . ($i + 1);
fwrite($sheetRelsFilePointer, '<Relationship Id="' . $refID . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="' . htmlentities($url) . '" TargetMode="External"/>' . PHP_EOL);
$i++;
}
fwrite($sheetRelsFilePointer, '</Relationships>');
fclose($sheetRelsFilePointer);
// Finish file
fwrite($this->sheetFilePointer, '</worksheet>');
fclose($this->sheetFilePointer);
}

View File

@ -34,11 +34,12 @@ interface WriterInterface
*
* @param array $dataRow Array containing data to be streamed.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return \Box\Spout\Writer\WriterInterface
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yetthe writer
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
public function addRow(array $dataRow);
public function addRow(array $dataRow, array $metaData = array());
/**
* Write given data to the output. New data will be appended to end of stream.

View File

@ -129,10 +129,11 @@ class XLS extends AbstractWriter
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow)
protected function addRowToWriter(array $dataRow, array $metaData)
{
if ($this->row == 65536) {
// Hit the limit. You should have chosen XLSX

View File

@ -160,14 +160,15 @@ class XLSX extends AbstractWriter
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow)
protected function addRowToWriter(array $dataRow, array $metaData)
{
$this->throwIfBookIsNotAvailable();
$this->book->addRowToCurrentWorksheet($dataRow);
$this->book->addRowToCurrentWorksheet($dataRow, $metaData);
}
/**