Skip to content

Commit

Permalink
Merge pull request #2 from rushi/XOL-2733
Browse files Browse the repository at this point in the history
XOL-2733 Better abstraction of report writers
  • Loading branch information
anush committed Jan 28, 2016
2 parents bb4f5c1 + 989d1df commit 0181375
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 50 deletions.
22 changes: 21 additions & 1 deletion Service/AbstractWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,25 @@ abstract class AbstractWriter
{
protected $logger;

/**
* Absolute path to the file that will be written by the writer
*
* @var string
*/
protected $filepath;

public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}

/**
* Initialize the writer
*
* @param string $filepath Path to the file that needs to be created by the writer
*/
abstract public function setup($filepath);

/**
* Write the formatted order data to disk, so we can fetch it later
*
Expand Down Expand Up @@ -83,4 +97,10 @@ private function findNestedHeader($key, $existingHeaders)
}

abstract public function prepare($cacheFile, $sortedHeaders);
}

abstract public function writeHeaders($headers, $initRow = null);

abstract public function writeRow($row, $headers = []);

abstract public function finalize();
}
48 changes: 42 additions & 6 deletions Service/CSVWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@

class CSVWriter extends AbstractWriter
{
/**
* A file system pointer resource that is typically created using fopen()
*
* @var resource
*/
private $handle;

public function setup($filepath)
{
$this->handle = fopen($filepath, 'w+');
}

/**
* Write the compiled csv to disk and return the file name
*
Expand All @@ -14,7 +26,7 @@ class CSVWriter extends AbstractWriter
public function prepare($cacheFile, $sortedHeaders)
{
$csvFile = $cacheFile . '.csv';
$handle = fopen($csvFile, 'w');
$this->setup($csvFile);

// Generate a csv version of the multi-row headers to write to disk
$headerRows = [[], []];
Expand All @@ -39,8 +51,8 @@ public function prepare($cacheFile, $sortedHeaders)
}
}

fputcsv($handle, $headerRows[0]);
fputcsv($handle, $headerRows[1]);
$this->writeRow($headerRows[0]);
$this->writeRow($headerRows[1]);

// TODO: Track memory usage
$file = new \SplFileObject($cacheFile);
Expand All @@ -65,15 +77,39 @@ public function prepare($cacheFile, $sortedHeaders)
}
}

fputcsv($handle, $csvRow);
$this->writeRow($csvRow);
$file->next();
}

$file = null; // Get rid of the file handle that SplFileObject has on cache file
unlink($cacheFile);

fclose($handle);
fclose($this->handle);

return $csvFile;
}
}

public function writeHeaders($headers, $initRow = null)
{
$contents = stream_get_contents($this->handle, -1, 0);
rewind($this->handle);
$this->writeRow($headers);
fwrite($this->handle, $contents);
}

/**
* Write a row to the csv
*
* @param array $row
* @param array $headers
*/
public function writeRow($row, $headers = [])
{
fputcsv($this->handle, $row);
}

public function finalize()
{
fclose($this->handle);
}
}
72 changes: 50 additions & 22 deletions Service/ExcelWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,22 @@ public function __construct(LoggerInterface $logger, PHPExcelFactory $phpExcel)
/**
* Initialize the excel writer
*
* @param string $author The author/creator of this file
* @param string $title The title of this file
* @param string $filepath
*/
public function setup($author = '', $title = '')
public function setup($filepath)
{
$this->handle->getActiveSheet()->getPageSetup()->setOrientation(\PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE);
$this->filepath = $filepath;
}

/**
* Set meta properties for the excel writer
*
* @param string $author The author/creator of this file
* @param string $title The title of this file
*/
public function setProperties($author = '', $title = '')
{
$this->handle->getProperties()
->setCreator($author)
->setTitle($title);
Expand Down Expand Up @@ -63,16 +72,23 @@ public function setSheetTitle($title)
/**
* Write the headers (nested or otherwise) to the current active worksheet
*
* @param $sortedHeaders
* @param $headers
* @param $initRow
*
* @throws \PHPExcel_Exception
*/
public function writeHeaders($sortedHeaders)
public function writeHeaders($headers, $initRow = null)
{
$worksheet = $this->handle->getActiveSheet();

$initRow = $this->currentRow;
if ($initRow) {
$worksheet->insertNewRowBefore($initRow, 2);
} else {
$initRow = $this->currentRow;
}

$column = 'A';
foreach ($sortedHeaders as $idx => $header) {
foreach ($headers as $idx => $header) {
$cell = $column . $initRow;
if (!is_array($header)) {
$worksheet->setCellValue($cell, $header);
Expand Down Expand Up @@ -140,8 +156,14 @@ public function writeRows($rows, $headers)
}
}

public function writeRow($dataRow, $headers)
public function writeRow($dataRow, $headers = [])
{
if (empty($headers)) {
// No headers to manage. Just write this array of data directly
$this->writeArray($dataRow);
return;
}

if (!is_array($dataRow)) {
// Invalid data -- don't process this row
return;
Expand All @@ -163,26 +185,33 @@ public function writeRow($dataRow, $headers)
}
}

$this->handle->getActiveSheet()->fromArray($excelRow, null, 'A' . $rowIdx);
$this->currentRow++;
$this->writeArrays($excelRow);
}

/**
* Write ad-hoc set of rows without any dependence on headers
* Write one or more rows starting at the given row and column
*
* @param array $lines
* @param int $row
* @param string $column
*/
public function writeRawRows(array $lines, $column = 'A', $row = null)
private function writeArrays(array $lines)
{
if (is_null($row)) {
$row = $this->currentRow;
}
$this->handle->getActiveSheet()->fromArray($lines, null, $column . $row);
$startCell = 'A' . $this->currentRow;
$this->handle->getActiveSheet()->fromArray($lines, null, $startCell);
$this->currentRow += count($lines);
}

/**
* Write a single row of data
*
* @param array $row A single row of data
*/
private function writeArray(array $row)
{
$startCell = 'A' . $this->currentRow;
$this->handle->getActiveSheet()->fromArray([$row], null, $startCell);
$this->currentRow++;
}

/**
* Freeze panes at the given location so they stay fixed upon scroll
*
Expand Down Expand Up @@ -214,17 +243,16 @@ public function addHorizontalPageBreak($cell = '')
/**
* Save the current data into an .xlsx file
*
* @param $filename
* @throws \PHPExcel_Exception
*/
public function finalize($filename)
public function finalize()
{
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$this->handle->setActiveSheetIndex(0);

// Write the file to disk
$writer = $this->phpexcel->createWriter($this->handle, 'Excel2007');
$writer->save($filename);
$writer->save($this->filepath);
}

public function resetCurrentRow($pos)
Expand All @@ -248,4 +276,4 @@ private function incrementColumn($str, $count = 1)

return $str;
}
}
}
42 changes: 42 additions & 0 deletions Tests/Service/CSVWriterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

use Xola\ReportWriterBundle\Service\CSVWriter;

class CSVWriterTest extends PHPUnit_Framework_TestCase
{
/** @var CSVWriter */
private $writer;
private $handle;
private $testfilename;

public function setUp()
{
$this->writer = $this->buildService();
$this->testfilename = uniqid() . ".csv";
$this->writer->setup($this->testfilename);
}

public function tearDown()
{
$this->writer->finalize();
unlink($this->testfilename);
}

public function buildService($params = [])
{
$defaults = ['logger' => $this->getMockBuilder('Psr\Log\LoggerInterface')->disableOriginalConstructor()->getMock()];
$params = array_merge($defaults, $params);

return new CSVWriter($params['logger']);
}

public function testShouldWriteRowToFile()
{
$this->writer->writeRow(['a', 'b', 'c,', 'd']);
$this->writer->writeRow(['e', 'f', 'g', 'h']);

$contents = file_get_contents($this->testfilename);
$expected = "a,b,\"c,\",d\ne,f,g,h\n";
$this->assertEquals($expected, $contents);
}
}
46 changes: 25 additions & 21 deletions Tests/Service/ExcelWriterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,29 @@ public function buildService($params = [])
}

public function testShouldInitializePHPExcelObject()
{
// Setup all the mocks
$pageSetupMock = $this->getMockBuilder('\PHPExcel_Worksheet_PageSetup')->disableOriginalConstructor()->getMock();
$pageSetupMock->expects($this->once())->method('setOrientation')->with('landscape');
$worksheetMock = $this->getMockBuilder('\PHPExcel_Worksheet')->disableOriginalConstructor()->getMock();
$worksheetMock->expects($this->once())->method('getPageSetup')->willReturn($pageSetupMock);
$this->phpExcelHandleMock->expects($this->once())->method('getActiveSheet')->willReturn($worksheetMock);

$this->buildService()->setup("filename.xlsx");
}

public function testShouldSetPropertiesForExcelFile()
{
$author = 'foo';
$title = 'bar';

// Setup all the mocks
$pageSetupMock = $this->getMockBuilder('\PHPExcel_Worksheet_PageSetup')->disableOriginalConstructor()->getMock();
$pageSetupMock->expects($this->once())->method('setOrientation')->with('landscape');
$propertiesMock = $this->getMockBuilder('\PHPExcel_DocumentProperties')->disableOriginalConstructor()->getMock();
$propertiesMock->expects($this->once())->method('setCreator')->with($author)->willReturn($propertiesMock);
$propertiesMock->expects($this->once())->method('setTitle')->with($title)->willReturn($propertiesMock);
$worksheetMock = $this->getMockBuilder('\PHPExcel_Worksheet')->disableOriginalConstructor()->getMock();
$worksheetMock->expects($this->once())->method('getPageSetup')->willReturn($pageSetupMock);
$this->phpExcelHandleMock->expects($this->once())->method('getActiveSheet')->willReturn($worksheetMock);
$this->phpExcelHandleMock->expects($this->once())->method('getProperties')->willReturn($propertiesMock);

$this->buildService()->setup($author, $title);
$this->buildService()->setProperties($author, $title);
}

public function testShouldSetCurrentWorksheet()
Expand Down Expand Up @@ -151,24 +158,14 @@ public function testShouldWriteNestedRowData()
$this->buildService()->writeRow($input, $headers);
}

public function testShouldWriteRawDataStartingFromTheFirstCell()
{
$lines = ['Lorem', 'Ipsum', 'Dolor', 'Sit', 'Amet'];
$worksheetMock = $this->getMockBuilder('\PHPExcel_Worksheet')->disableOriginalConstructor()->getMock();
$worksheetMock->expects($this->once())->method('fromArray')->with($lines, null, 'A1');
$this->phpExcelHandleMock->expects($this->once())->method('getActiveSheet')->willReturn($worksheetMock);

$this->buildService()->writeRawRows($lines);
}

public function testShouldWriteRawDataStartingFromTheGivenCell()
public function testShouldWriteArrayAsDataStartingFromTheFirstCell()
{
$lines = ['Lorem', 'Ipsum', 'Dolor', 'Sit', 'Amet'];
$worksheetMock = $this->getMockBuilder('\PHPExcel_Worksheet')->disableOriginalConstructor()->getMock();
$worksheetMock->expects($this->once())->method('fromArray')->with($lines, null, 'C3');
$worksheetMock->expects($this->once())->method('fromArray')->with([$lines], null, 'A1');
$this->phpExcelHandleMock->expects($this->once())->method('getActiveSheet')->willReturn($worksheetMock);

$this->buildService()->writeRawRows($lines, 'C', 3);
$this->buildService()->writeRow($lines);
}

public function testShouldFreezePanes()
Expand Down Expand Up @@ -210,14 +207,21 @@ public function testShouldSaveFileInExcel2007Format()
$writerMock = $this->getMockBuilder('\PHPExcel_Writer_IWriter')->disableOriginalConstructor()->getMock();
$writerMock->expects($this->once())->method('save')->with($filename);

// Setup all the mocks
$pageSetupMock = $this->getMockBuilder('\PHPExcel_Worksheet_PageSetup')->disableOriginalConstructor()->getMock();
$worksheetMock = $this->getMockBuilder('\PHPExcel_Worksheet')->disableOriginalConstructor()->getMock();
$worksheetMock->expects($this->once())->method('getPageSetup')->willReturn($pageSetupMock);
$this->phpExcelHandleMock->expects($this->once())->method('getActiveSheet')->willReturn($worksheetMock);

$phpExcel = $this->getPHPExcelMock();
$phpExcel->expects($this->once())->method('createWriter')
->with($this->phpExcelHandleMock, 'Excel2007')
->willReturn($writerMock);

$service = $this->buildService(['phpExcel' => $phpExcel]);
$service->setup($filename);

$service->finalize($filename);
$service->finalize();
}

private function getPHPExcelMock()
Expand All @@ -227,4 +231,4 @@ private function getPHPExcelMock()

return $phpExcel;
}
}
}

0 comments on commit 0181375

Please sign in to comment.