From f8d14d3f05b71867089a773fda9afde265b298cb Mon Sep 17 00:00:00 2001 From: Marcos Teodoro Date: Sun, 24 Nov 2019 02:32:38 -0300 Subject: [PATCH] Enable support to XLSM spreadsheets --- .../Reader/Common/Creator/ReaderFactory.php | 30 +++++-- tests/Spout/Reader/XLSM/ReaderTest.php | 73 ++++++++++++++++++ tests/Spout/TestUsingResource.php | 2 +- tests/resources/xlsm/simple-xlsm-sample.xlsm | Bin 0 -> 8593 bytes 4 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 tests/Spout/Reader/XLSM/ReaderTest.php create mode 100644 tests/resources/xlsm/simple-xlsm-sample.xlsm diff --git a/src/Spout/Reader/Common/Creator/ReaderFactory.php b/src/Spout/Reader/Common/Creator/ReaderFactory.php index 6b10175..c16d469 100644 --- a/src/Spout/Reader/Common/Creator/ReaderFactory.php +++ b/src/Spout/Reader/Common/Creator/ReaderFactory.php @@ -28,6 +28,16 @@ use Box\Spout\Reader\XLSX\Reader as XLSXReader; */ class ReaderFactory { + /** + * File extensions and readers mapped + */ + protected const MAPPED_EXTENSIONS = [ + 'xlsx' => 'xlsx', + 'csv' => 'csv', + 'ods' => 'ods', + 'xlsm' => 'xlsx', + ]; + /** * Creates a reader by file extension * @@ -42,6 +52,15 @@ class ReaderFactory return self::createFromType($extension); } + public static function getMappedFormats(string $fileExtension) + { + if (!(array_key_exists($fileExtension, self::MAPPED_EXTENSIONS))) { + throw new UnsupportedTypeException('No readers supporting the given type: ' . $fileExtension); + } + + return self::MAPPED_EXTENSIONS[$fileExtension]; + } + /** * This creates an instance of the appropriate reader, given the type of the file to be read * @@ -51,13 +70,10 @@ class ReaderFactory */ public static function createFromType($readerType) { - switch ($readerType) { - case Type::CSV: return self::createCSVReader(); - case Type::XLSX: return self::createXLSXReader(); - case Type::ODS: return self::createODSReader(); - default: - throw new UnsupportedTypeException('No readers supporting the given type: ' . $readerType); - } + $readerType = self::getMappedFormats($readerType); + $methodName = 'create' . strtoupper($readerType) . 'Reader'; + + return self::$methodName(); } /** diff --git a/tests/Spout/Reader/XLSM/ReaderTest.php b/tests/Spout/Reader/XLSM/ReaderTest.php new file mode 100644 index 0000000..6e15382 --- /dev/null +++ b/tests/Spout/Reader/XLSM/ReaderTest.php @@ -0,0 +1,73 @@ +getResourcePath('simple-xlsm-sample.xlsm'); + $reader = ReaderEntityFactory::createReaderFromFile($resourcePath); + + $this->assertInstanceOf(Box\Spout\Reader\ReaderInterface::class, $reader); + } + + /** + * @return void + */ + public function testReadXLSMSpreadsheetShouldReturnData() + { + $this->assertNotEmpty($this->getAllRowsForFile('simple-xlsm-sample.xlsm')); + } + + /** + * @return void + */ + public function testSpreadsheetRowsShouldMatch() + { + $expectedRows = [ + ['a1', 'b1', 'c1'], + ['a2', 'b2', 'c2'], + ['a3', 'b3', 'c3'], + ]; + + $this->assertEquals($expectedRows, $this->getAllRowsForFile('simple-xlsm-sample.xlsm')); + } + + /** + * @param string $fileName + * @param bool $shouldFormatDates + * @param bool $shouldPreserveEmptyRows + * @return array All the read rows the given file + */ + private function getAllRowsForFile($fileName, $shouldFormatDates = false, $shouldPreserveEmptyRows = false) + { + $allRows = []; + $resourcePath = $this->getResourcePath($fileName); + + $reader = ReaderEntityFactory::createXLSXReader(); + $reader->setShouldFormatDates($shouldFormatDates); + $reader->setShouldPreserveEmptyRows($shouldPreserveEmptyRows); + $reader->open($resourcePath); + + foreach ($reader->getSheetIterator() as $sheetIndex => $sheet) { + foreach ($sheet->getRowIterator() as $rowIndex => $row) { + $allRows[] = $row->toArray(); + } + } + + $reader->close(); + + return $allRows; + } +} diff --git a/tests/Spout/TestUsingResource.php b/tests/Spout/TestUsingResource.php index 551f86b..defebc0 100644 --- a/tests/Spout/TestUsingResource.php +++ b/tests/Spout/TestUsingResource.php @@ -8,7 +8,7 @@ namespace Box\Spout; trait TestUsingResource { /** @var string Path to the test resources folder */ - private $resourcesPath = 'tests/resources'; + private $resourcesPath = __DIR__ . '/../../tests/resources'; /** @var string Path to the test generated resources folder */ private $generatedResourcesPath = 'tests/resources/generated'; diff --git a/tests/resources/xlsm/simple-xlsm-sample.xlsm b/tests/resources/xlsm/simple-xlsm-sample.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..028b1fb2d0062882da8726015af562c9b031628a GIT binary patch literal 8593 zcma)h1yo$i((d31?(Pik5Zv9}T_?fa-Q6_=2p-%C?m+_y0fGcVaF>A)9Kt^&@1AqQ zf8Kqs*P6X&_Ec5x{<^BVR(+}pFtCV#hY_i~Ci-yy&v**?V(Dn1>f-3+%Bl(x!-hO~ z@=MIbS3&(56aXLu4FI73Q_RfCiN(v_E;qSPet-=<0vrNHcQI+~h#E=pz>w_Jo7b9P z&vk&Y$K~QF(12a z)~!!R%a^Z$dQyU4`JDr?-oHSx*U+PBM>G`)`GdrP$o7FU#g8@uuqNAk^>MJhHNn;p zRru}t+11_Q$?2|COj&7*8L`1|Bj3jtp%kb$L&D_Fl4f~M_GM6=6^J-HXe(dvy-a0W zpO#vsaVb?#ePLfy!3l4AjS;M{pw>L)lSn2V6XMlMt zozL3Efn#dWuZSqln#CvfK|b-AWG`{6*a;cj0ExI~n^L39n?rp?oVbQbE2*+muU!lZ zjGkneA6%hgpX#~o&^g3uFbz~MnvY!yEJCwQgVpStV7EMo4lko3PQM$w8Itu=zx0-^qd z8XNV3`5ppsAOvo#KcIGZb#t`;<5cej= zN-^gasHNuU;q}_YiM?k{v29*xTVFK~aWD#a$7-4suz_>ClhVA8nUe37-4cblkblb( zC<>$kI;?qQia3Z+uI!o%kFO6T_QU^JiyiIcbF`H|(vY;q=8kJ% zQj)*TOuv*VU^&_IFsk|a%QgQ!N5DYv^0NDt9sXSa^|yehql>Mp6$s?^IFoE<%)F+6 z`1nUy0094g$^KGd|K;wQN{&Tr*qyaa_fS{H$a0I%FjU`4PS!LrYTbByj8o~dbK43O z++ExRt%XcUIGz*vK7YMEI(p~I9LkB$*d#AUEDZzJ0npzaAgtT~c3CDDLO)fbaD z?oGKlI4v!n*|K5;;E{(m(3j`fa;NDVi+!*wBT|h@TlhS_I!jz;k6c&Sqp8pyRcN5k zSuoOD$4<8T2}I9p$0ah8|5emnlq2$|JvE^~gAg9s8Y8l8{oLflz?z4spoyRVYAx

Apf@1jMZ^#$}05)K1HV2|>9 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 literal 0 HcmV?d00001