Cell value objects (#383)
* first stab at cell objects, #182 * removed comment parameter, streamlined cell detection, more tests #182 * shorter constant names, missing isFormula() #182 * first batch of changes #182 * documentation #182
This commit is contained in:
parent
4e6db6a8a1
commit
4acd9ad087
166
src/Spout/Writer/Common/Cell.php
Normal file
166
src/Spout/Writer/Common/Cell.php
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Box\Spout\Writer\Common;
|
||||||
|
|
||||||
|
use Box\Spout\Writer\Common\Helper\CellHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Cell
|
||||||
|
*
|
||||||
|
* @package Box\Spout\Writer\Common
|
||||||
|
*/
|
||||||
|
class Cell
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Numeric cell type (whole numbers, fractional numbers, dates)
|
||||||
|
*/
|
||||||
|
const TYPE_NUMERIC = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String (text) cell type
|
||||||
|
*/
|
||||||
|
const TYPE_STRING = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formula cell type
|
||||||
|
* Not used at the moment
|
||||||
|
*/
|
||||||
|
const TYPE_FORMULA = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty cell type
|
||||||
|
*/
|
||||||
|
const TYPE_EMPTY = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean cell type
|
||||||
|
*/
|
||||||
|
const TYPE_BOOLEAN = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error cell type
|
||||||
|
*/
|
||||||
|
const TYPE_ERROR = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of this cell
|
||||||
|
* @var mixed|null
|
||||||
|
*/
|
||||||
|
protected $value = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cell type
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
protected $type = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cell constructor.
|
||||||
|
* @param $value mixed
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
$this->setValue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $value mixed
|
||||||
|
*/
|
||||||
|
public function setValue($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
$this->type = $this->detectType($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed|null
|
||||||
|
*/
|
||||||
|
public function getValue()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current value type
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function detectType($value)
|
||||||
|
{
|
||||||
|
if (CellHelper::isBoolean($value)) {
|
||||||
|
return self::TYPE_BOOLEAN;
|
||||||
|
} elseif (CellHelper::isEmpty($value)) {
|
||||||
|
return self::TYPE_EMPTY;
|
||||||
|
} elseif (CellHelper::isNumeric($this->getValue())) {
|
||||||
|
return self::TYPE_NUMERIC;
|
||||||
|
} elseif (CellHelper::isNonEmptyString($value)) {
|
||||||
|
return self::TYPE_STRING;
|
||||||
|
} else {
|
||||||
|
return self::TYPE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isBoolean()
|
||||||
|
{
|
||||||
|
return $this->type === self::TYPE_BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
return $this->type === self::TYPE_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not used at the moment
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isFormula()
|
||||||
|
{
|
||||||
|
return $this->type === self::TYPE_FORMULA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isNumeric()
|
||||||
|
{
|
||||||
|
return $this->type === self::TYPE_NUMERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isString()
|
||||||
|
{
|
||||||
|
return $this->type === self::TYPE_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isError()
|
||||||
|
{
|
||||||
|
return $this->type === self::TYPE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return (string)$this->value;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace Box\Spout\Writer\ODS\Internal;
|
|||||||
use Box\Spout\Common\Exception\InvalidArgumentException;
|
use Box\Spout\Common\Exception\InvalidArgumentException;
|
||||||
use Box\Spout\Common\Exception\IOException;
|
use Box\Spout\Common\Exception\IOException;
|
||||||
use Box\Spout\Common\Helper\StringHelper;
|
use Box\Spout\Common\Helper\StringHelper;
|
||||||
|
use Box\Spout\Writer\Common\Cell;
|
||||||
use Box\Spout\Writer\Common\Helper\CellHelper;
|
use Box\Spout\Writer\Common\Helper\CellHelper;
|
||||||
use Box\Spout\Writer\Common\Internal\WorksheetInterface;
|
use Box\Spout\Writer\Common\Internal\WorksheetInterface;
|
||||||
|
|
||||||
@ -191,27 +192,34 @@ class Worksheet implements WorksheetInterface
|
|||||||
$data .= ' table:number-columns-repeated="' . $numTimesValueRepeated . '"';
|
$data .= ' table:number-columns-repeated="' . $numTimesValueRepeated . '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CellHelper::isNonEmptyString($cellValue)) {
|
/** @TODO Remove code duplication with XLSX writer: https://github.com/box/spout/pull/383#discussion_r113292746 */
|
||||||
|
if ($cellValue instanceof Cell) {
|
||||||
|
$cell = $cellValue;
|
||||||
|
} else {
|
||||||
|
$cell = new Cell($cellValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cell->isString()) {
|
||||||
$data .= ' office:value-type="string" calcext:value-type="string">';
|
$data .= ' office:value-type="string" calcext:value-type="string">';
|
||||||
|
|
||||||
$cellValueLines = explode("\n", $cellValue);
|
$cellValueLines = explode("\n", $cell->getValue());
|
||||||
foreach ($cellValueLines as $cellValueLine) {
|
foreach ($cellValueLines as $cellValueLine) {
|
||||||
$data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
|
$data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$data .= '</table:table-cell>';
|
$data .= '</table:table-cell>';
|
||||||
} else if (CellHelper::isBoolean($cellValue)) {
|
} else if ($cell->isBoolean()) {
|
||||||
$data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $cellValue . '">';
|
$data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $cell->getValue() . '">';
|
||||||
$data .= '<text:p>' . $cellValue . '</text:p>';
|
$data .= '<text:p>' . $cell->getValue() . '</text:p>';
|
||||||
$data .= '</table:table-cell>';
|
$data .= '</table:table-cell>';
|
||||||
} else if (CellHelper::isNumeric($cellValue)) {
|
} else if ($cell->isNumeric()) {
|
||||||
$data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cellValue . '">';
|
$data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cell->getValue() . '">';
|
||||||
$data .= '<text:p>' . $cellValue . '</text:p>';
|
$data .= '<text:p>' . $cell->getValue() . '</text:p>';
|
||||||
$data .= '</table:table-cell>';
|
$data .= '</table:table-cell>';
|
||||||
} else if (empty($cellValue)) {
|
} else if ($cell->isEmpty()) {
|
||||||
$data .= '/>';
|
$data .= '/>';
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue));
|
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
@ -5,6 +5,7 @@ namespace Box\Spout\Writer\XLSX\Internal;
|
|||||||
use Box\Spout\Common\Exception\InvalidArgumentException;
|
use Box\Spout\Common\Exception\InvalidArgumentException;
|
||||||
use Box\Spout\Common\Exception\IOException;
|
use Box\Spout\Common\Exception\IOException;
|
||||||
use Box\Spout\Common\Helper\StringHelper;
|
use Box\Spout\Common\Helper\StringHelper;
|
||||||
|
use Box\Spout\Writer\Common\Cell;
|
||||||
use Box\Spout\Writer\Common\Helper\CellHelper;
|
use Box\Spout\Writer\Common\Helper\CellHelper;
|
||||||
use Box\Spout\Writer\Common\Internal\WorksheetInterface;
|
use Box\Spout\Writer\Common\Internal\WorksheetInterface;
|
||||||
|
|
||||||
@ -213,13 +214,20 @@ EOD;
|
|||||||
$cellXML = '<c r="' . $columnIndex . $rowIndex . '"';
|
$cellXML = '<c r="' . $columnIndex . $rowIndex . '"';
|
||||||
$cellXML .= ' s="' . $styleId . '"';
|
$cellXML .= ' s="' . $styleId . '"';
|
||||||
|
|
||||||
if (CellHelper::isNonEmptyString($cellValue)) {
|
/** @TODO Remove code duplication with ODS writer: https://github.com/box/spout/pull/383#discussion_r113292746 */
|
||||||
$cellXML .= $this->getCellXMLFragmentForNonEmptyString($cellValue);
|
if ($cellValue instanceof Cell) {
|
||||||
} else if (CellHelper::isBoolean($cellValue)) {
|
$cell = $cellValue;
|
||||||
$cellXML .= ' t="b"><v>' . intval($cellValue) . '</v></c>';
|
} else {
|
||||||
} else if (CellHelper::isNumeric($cellValue)) {
|
$cell = new Cell($cellValue);
|
||||||
$cellXML .= '><v>' . $cellValue . '</v></c>';
|
}
|
||||||
} else if (empty($cellValue)) {
|
|
||||||
|
if ($cell->isString()) {
|
||||||
|
$cellXML .= $this->getCellXMLFragmentForNonEmptyString($cell->getValue());
|
||||||
|
} else if ($cell->isBoolean()) {
|
||||||
|
$cellXML .= ' t="b"><v>' . intval($cell->getValue()) . '</v></c>';
|
||||||
|
} else if ($cell->isNumeric()) {
|
||||||
|
$cellXML .= '><v>' . $cell->getValue() . '</v></c>';
|
||||||
|
} else if ($cell->isEmpty()) {
|
||||||
if ($this->styleHelper->shouldApplyStyleOnEmptyCell($styleId)) {
|
if ($this->styleHelper->shouldApplyStyleOnEmptyCell($styleId)) {
|
||||||
$cellXML .= '/>';
|
$cellXML .= '/>';
|
||||||
} else {
|
} else {
|
||||||
@ -228,7 +236,7 @@ EOD;
|
|||||||
$cellXML = '';
|
$cellXML = '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue));
|
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cellXML;
|
return $cellXML;
|
||||||
|
@ -5,6 +5,7 @@ namespace Box\Spout\Writer\CSV;
|
|||||||
use Box\Spout\TestUsingResource;
|
use Box\Spout\TestUsingResource;
|
||||||
use Box\Spout\Common\Type;
|
use Box\Spout\Common\Type;
|
||||||
use Box\Spout\Common\Helper\EncodingHelper;
|
use Box\Spout\Common\Helper\EncodingHelper;
|
||||||
|
use Box\Spout\Writer\Common\Cell;
|
||||||
use Box\Spout\Writer\WriterFactory;
|
use Box\Spout\Writer\WriterFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,6 +178,19 @@ class WriterTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('#This is, a comma#,csv--12,csv--13', $writtenContent, 'The fields should be enclosed with #');
|
$this->assertEquals('#This is, a comma#,csv--12,csv--13', $writtenContent, 'The fields should be enclosed with #');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testWriteShouldAcceptCellObjects()
|
||||||
|
{
|
||||||
|
$allRows = [
|
||||||
|
[new Cell('String Value'), new Cell(1)]
|
||||||
|
];
|
||||||
|
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_cell_objects.csv');
|
||||||
|
$writtenContent = $this->trimWrittenContent($writtenContent);
|
||||||
|
$this->assertEquals('"String Value",1', $writtenContent);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $allRows
|
* @param array $allRows
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
|
@ -6,6 +6,7 @@ use Box\Spout\Common\Exception\SpoutException;
|
|||||||
use Box\Spout\Common\Type;
|
use Box\Spout\Common\Type;
|
||||||
use Box\Spout\Reader\Wrapper\XMLReader;
|
use Box\Spout\Reader\Wrapper\XMLReader;
|
||||||
use Box\Spout\TestUsingResource;
|
use Box\Spout\TestUsingResource;
|
||||||
|
use Box\Spout\Writer\Common\Cell;
|
||||||
use Box\Spout\Writer\Common\Helper\ZipHelper;
|
use Box\Spout\Writer\Common\Helper\ZipHelper;
|
||||||
use Box\Spout\Writer\WriterFactory;
|
use Box\Spout\Writer\WriterFactory;
|
||||||
|
|
||||||
@ -463,6 +464,47 @@ class WriterTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('application/vnd.oasis.opendocument.spreadsheet', $finfo->file($resourcePath));
|
$this->assertEquals('application/vnd.oasis.opendocument.spreadsheet', $finfo->file($resourcePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testWriteShouldAcceptCellObjects()
|
||||||
|
{
|
||||||
|
$fileName = 'test_writer_should_accept_cell_objects.ods';
|
||||||
|
$dataRows = [
|
||||||
|
[new Cell('ods--11'), new Cell('ods--12')],
|
||||||
|
[new Cell('ods--21'), new Cell('ods--22'), new Cell('ods--23')],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->writeToODSFile($dataRows, $fileName);
|
||||||
|
|
||||||
|
foreach ($dataRows as $dataRow) {
|
||||||
|
/** @var Cell $cell */
|
||||||
|
foreach ($dataRow as $cell) {
|
||||||
|
$this->assertValueWasWritten($fileName, $cell->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testWriteShouldAcceptCellObjectsWithDifferentValueTypes()
|
||||||
|
{
|
||||||
|
$fileName = 'test_writer_should_accept_cell_objects_with_types.ods';
|
||||||
|
$dataRows = [
|
||||||
|
[new Cell('i am a string'), new Cell(51465), new Cell(true), new Cell(51465.5)],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->writeToODSFile($dataRows, $fileName);
|
||||||
|
|
||||||
|
foreach ($dataRows as $dataRow) {
|
||||||
|
/** @var Cell $cell */
|
||||||
|
foreach ($dataRow as $cell) {
|
||||||
|
$this->assertValueWasWritten($fileName, (string)$cell->getValue(), '', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $allRows
|
* @param array $allRows
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
@ -527,6 +569,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
|
|||||||
$xmlContents = file_get_contents('zip://' . $pathToContentFile);
|
$xmlContents = file_get_contents('zip://' . $pathToContentFile);
|
||||||
|
|
||||||
$this->assertContains($value, $xmlContents, $message);
|
$this->assertContains($value, $xmlContents, $message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,6 +5,7 @@ namespace Box\Spout\Writer\XLSX;
|
|||||||
use Box\Spout\Common\Exception\SpoutException;
|
use Box\Spout\Common\Exception\SpoutException;
|
||||||
use Box\Spout\Common\Type;
|
use Box\Spout\Common\Type;
|
||||||
use Box\Spout\TestUsingResource;
|
use Box\Spout\TestUsingResource;
|
||||||
|
use Box\Spout\Writer\Common\Cell;
|
||||||
use Box\Spout\Writer\WriterFactory;
|
use Box\Spout\Writer\WriterFactory;
|
||||||
use Box\Spout\Writer\XLSX\Internal\Worksheet;
|
use Box\Spout\Writer\XLSX\Internal\Worksheet;
|
||||||
|
|
||||||
@ -507,6 +508,60 @@ class WriterTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $finfo->file($resourcePath));
|
$this->assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $finfo->file($resourcePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testWriterShouldAcceptCellObjects()
|
||||||
|
{
|
||||||
|
$fileName = 'test_writer_should_accept_cell_objects.xlsx';
|
||||||
|
$dataRows = [
|
||||||
|
[new Cell('xlsx--11'), new Cell('xlsx--12')],
|
||||||
|
[new Cell('xlsx--21'), new Cell('xlsx--22'), new Cell('xlsx--23')],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = false);
|
||||||
|
|
||||||
|
foreach ($dataRows as $dataRow) {
|
||||||
|
/** @var Cell $cell */
|
||||||
|
foreach ($dataRow as $cell) {
|
||||||
|
$this->assertSharedStringWasWritten($fileName, $cell->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testWriteShouldAcceptCellObjectsWithDifferentValueTypes()
|
||||||
|
{
|
||||||
|
$fileName = 'test_writer_should_accept_cell_objects_with_types.xlsx';
|
||||||
|
|
||||||
|
$dataRowsShared = [
|
||||||
|
[new Cell('i am a string')],
|
||||||
|
];
|
||||||
|
$dataRowsInline = [
|
||||||
|
[new Cell(51465), new Cell(true), new Cell(51465.5)]
|
||||||
|
];
|
||||||
|
|
||||||
|
$dataRows = array_merge($dataRowsShared, $dataRowsInline);
|
||||||
|
|
||||||
|
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = false);
|
||||||
|
|
||||||
|
foreach ($dataRowsShared as $dataRow) {
|
||||||
|
/** @var Cell $cell */
|
||||||
|
foreach ($dataRow as $cell) {
|
||||||
|
$this->assertSharedStringWasWritten($fileName, (string)$cell->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($dataRowsInline as $dataRow) {
|
||||||
|
/** @var Cell $cell */
|
||||||
|
foreach ($dataRow as $cell) {
|
||||||
|
$this->assertInlineDataWasWrittenToSheet($fileName, 1, $cell->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $allRows
|
* @param array $allRows
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
|
Loading…
x
Reference in New Issue
Block a user