2 namespace Utility\Export;
7 * For excel report building.
8 * @author andre.fourie, tjaart.viljoen
15 * @var Struct_Abstract_Report
17 protected $_objReport = null;
20 * @var PHPExcel_Writer_Excel2007
22 protected $_objWriter = null;
27 protected $_objPHPExcel = null;
30 * Filename to use for output
33 protected $_fileName = null;
41 * Report query details.
44 protected $_queries = array();
49 protected $_headers = array();
54 protected $_fields = array();
56 * Columns to provide totals for.
59 protected $_totals = array();
61 * Columns for currency format.
64 protected $_currencyFields = array();
66 * Columns totals accumulated.
69 protected $_totalAccum = array();
74 protected $_notes = array();
76 * Chart from report data.
79 protected $_chart = array();
84 protected $_data = array();
89 protected $numberToColumn = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ','BA','BB','BC','BD','BE','BF','BG','BH','BI','BJ','BK','BL','BM','BN','BO','BP','BQ','BR','BS','BT','BU','BV','BW','BX','BY','BZ','CA','CB','CC','CD','CE','CF','CG','CH','CI','CJ','CK','CL','CM','CN','CO','CP','CQ','CR','CS','CT','CU','CV','CW','CX','CY','CZ');
94 * @param \Utility\Service\Report $report
96 public function __construct(\Utility\Service\Report $report)
98 $this->_fileName = str_replace(' ', '-', $report->getTitle());
99 $objPHPExcel = new \PHPExcel();
100 $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
101 $this->_objReport = $report;
102 $this->_format = $report->getFormat();
103 $this->_queries = $report->getQueries();
104 $this->_headers = $report->getHeaders();
105 $this->_fields = $report->getFields();
106 $this->_totals = $report->getTotalFields();
107 $this->_currencyFields = $report->getCurrencyFields();
108 $this->_notes = $report->getNotes();
109 $this->_chart = $report->getChart();
110 $this->_data = $report->getData();
111 $this->_objWriter = $objWriter;
112 $this->_objPHPExcel = $objPHPExcel;
113 $this->_objPHPExcel->getProperties()->setCreator(APPLICATION);
114 $this->_objPHPExcel->getProperties()->setLastModifiedBy(APPLICATION);
115 $this->_objPHPExcel->getProperties()->setTitle($report->getTitle());
116 $this->_objPHPExcel->getProperties()->setSubject($report->getSubject());
117 $this->_objPHPExcel->getProperties()->setDescription($report->getDescription());
119 $numQueries = count($this->_queries);
120 $numRecords = count($this->_data);
121 $numNotes = count($this->_notes);
122 $numChart = !empty($this->_chart)
124 $numTotals = (!empty($this->_totals))
129 $this->_writeQueries(2);
132 $this->_createHeaders(2 + $numQueries);
133 $this->_writeRecords(3 + $numQueries);
134 if (!empty($this->_totals))
136 $this->_writeTotals(3 + $numQueries + $numRecords);
138 $this->_setCurrencyFormat(3 + $numQueries, $numRecords + $numTotals);
142 5 + $numQueries + $numRecords,
144 2 + $numQueries + $numRecords
146 $this->_objWriter->setIncludeCharts(true);
151 $this->_writeNotes(5 + $numQueries + $numRecords + $numChart);
158 * @param string $format
159 * @return Struct_Abstract_ExcelReport
161 public function setFormat($format)
163 $this->_format = $format;
169 * @todo add email, filesystem and ftp as potential targets.
170 * @param null|string $target
172 public function output($target = null)
175 ini_set('memory_limit', '-1');
177 if (is_null($target))
180 header("Content-Type: application/vnd.ms-excel");
181 header('Content-Disposition: attachment; filename="' .$this->_fileName.'_'. time() . '.xlsx"' );
182 $this->_objWriter->save('php://output');
184 elseif (strpos($target, '@'))
191 $this->_objWriter->save($target);
196 * Add logo to top of spreadsheet.
198 protected function _addLogo()
200 $totalCols = count($this->_headers);
202 // Add a drawing to the worksheet
203 $objDrawing = new \PHPExcel_Worksheet_Drawing();
204 $objDrawing->setName('Logo');
205 $objDrawing->setDescription('Logo');
206 $objDrawing->setPath(__DIR__ . '/../../../../../public/img/app/logos/logo-excel.png');
207 $objDrawing->setHeight(56);
209 $this->_objPHPExcel->getActiveSheet()->getRowDimension(1)->setRowHeight(56);
212 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
213 'color' => array('rgb'=>'222222'),
216 $style_header = array(
218 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
219 'color' => array('rgb'=>'bbbbbb'),
226 $convertTotalToString = $this->numberToColumn[$totalCols-1];
227 $this->_objPHPExcel->getActiveSheet()->getStyle("A1:{$convertTotalToString}1")->applyFromArray($style_logo);
229 $objDrawing->setWorksheet($this->_objPHPExcel->getActiveSheet());
233 * Set text wrapping for specified column.
234 * @param integer $column
235 * @param boolean $wrap
237 protected function _setColumnWrapping($column, $wrap = true)
239 $this->_objPHPExcel->getActiveSheet()->getStyle($column)->getAlignment()->setWrapText($wrap);
242 protected function _setCurrencyFormat($fromRow, $numRows)
244 foreach ($this->_currencyFields as $field)
246 $endRow = $fromRow + $numRows;
247 $labelInd = array_search($field, $this->_fields);
248 $labelInd = $this->numberToColumn[$labelInd];
251 ->getStyle($labelInd . $fromRow . ':' . $labelInd . $endRow)
253 ->setFormatCode(\Utility\Definitions\Locale::getExcelCurrencyFormat());
258 * Insert report query details.
259 * @param integer $lineNumber
261 protected function _writeQueries($lineNumber)
263 $convertTotalToString = $this->numberToColumn[count($this->_headers)-1];
264 $this->_objPHPExcel->getActiveSheet()->setCellValue('A'.$lineNumber, 'Filters applied to report:');
265 $style_header = array(
267 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
268 'color' => array('rgb'=>'aaaaaa'),
274 $this->_objPHPExcel->getActiveSheet()->getStyle("A$lineNumber:$convertTotalToString".$lineNumber)->applyFromArray($style_header);
277 foreach ($this->_queries as $param => $value)
279 $this->_objPHPExcel->getActiveSheet()->setCellValue('A'.$lineNumber, $param);
280 $this->_objPHPExcel->getActiveSheet()->setCellValue('B'.$lineNumber, $value);
281 $this->_objPHPExcel->getActiveSheet()->getStyle('A'.$lineNumber)->applyFromArray(
283 'font' => array('bold' => true),
284 'alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_LEFT),
286 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
287 'color' => array('rgb'=>'dddddd'),
290 $this->_objPHPExcel->getActiveSheet()->getStyle('A'.$lineNumber.':'.$convertTotalToString.$lineNumber)->applyFromArray(
292 'alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_LEFT),
294 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
295 'color' => array('rgb'=>'dddddd'),
303 * Insert report totals.
304 * @param integer $lineNumber
306 protected function _writeTotals($lineNumber)
309 $convertTotalToString = $this->numberToColumn[count($this->_headers)-1];
310 $this->_objPHPExcel->getActiveSheet()->getStyle($this->numberToColumn[$i].$lineNumber.':'.$convertTotalToString.$lineNumber)->applyFromArray(
312 'font' => array('bold' => true),
313 'alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_RIGHT),
315 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
316 'color' => array('rgb'=>'dddddd')
320 foreach ($this->_fields as $field)
322 if (isset($this->_totalAccum[$field]))
324 $this->_objPHPExcel->getActiveSheet()->setCellValue(
325 $this->numberToColumn[$i].$lineNumber,
326 $this->_totalAccum[$field]
334 * Insert report notes.
335 * @param integer $lineNumber
337 protected function _writeNotes($lineNumber)
339 foreach ($this->_notes as $note)
341 $this->_objPHPExcel->getActiveSheet()->setCellValue('A'.$lineNumber, $note);
342 $this->_objPHPExcel->getActiveSheet()->getStyle('A'.$lineNumber)->applyFromArray(
344 'font' => array('bold' => true),
345 'alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_LEFT)
353 * Insert column headers.
354 * @param integer $lineNumber
356 protected function _createHeaders($lineNumber)
360 foreach($this->_headers as $key => $val)
362 $this->_objPHPExcel->getActiveSheet()->setCellValue($this->numberToColumn[$i].$lineNumber, $val);
363 $this->_objPHPExcel->getActiveSheet()->getStyle($this->numberToColumn[$i].$lineNumber)->applyFromArray(array('alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_LEFT) ) );
368 $this->_objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(30);
369 for ($i = 1; $i < count($this->_headers); $i++)
371 $this->_objPHPExcel->getActiveSheet()->getColumnDimension($this->numberToColumn[$i])->setAutoSize(true);
373 $this->_objPHPExcel->getActiveSheet()->duplicateStyleArray(
375 'font' => array('bold' => true),
376 'alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_LEFT),
378 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
379 'color' => array('rgb'=>'aaaaaa')
382 $this->numberToColumn[0]. $lineNumber .':'.$this->numberToColumn[$i - 1]. $lineNumber
387 * Write data rows starting at specified line number.
388 * @param integer $lineNumber
390 protected function _writeRecords($lineNumber)
392 //[green]$#,##0.00;[red]$(-#,##0.00)
393 if (!empty($this->_totals))
395 foreach ($this->_totals as $field)
397 $this->_totalAccum[$field] = 0;
401 foreach ($this->_data as $record)
413 if (!empty($this->_totals))
415 foreach ($this->_totals as $field)
417 if (is_numeric($record[$field]))
419 $this->_totalAccum[$field] += $record[$field];
424 $tmpRecord = array();
425 $style = isset($record['excel.rowStyle'])
426 ? $record['excel.rowStyle']
428 foreach($this->_fields as $field)
430 $tmpRecord[] = $record[$field];
432 $this->_insertValues($tmpRecord, $lineNumber, 0, $style);
439 * Insert cell values for a data row.
440 * @param array $values
441 * @param integer $lineNumber
442 * @param integer $offset
443 * @param string|false $style
445 protected function _insertValues($values, $lineNumber, $offset = 0, $style = false)
448 if ('Totals' == $style)
450 $convertTotalToString = $this->numberToColumn[count($this->_headers)-1];
451 $this->_objPHPExcel->getActiveSheet()->getStyle($this->numberToColumn[$i].$lineNumber.':'.$convertTotalToString.$lineNumber)->applyFromArray(
453 'font' => array('bold' => true),
455 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
456 'color' => array('rgb'=>'dddddd')
461 if ('Titles' == $style)
463 $convertTotalToString = $this->numberToColumn[count($this->_headers)-1];
464 $this->_objPHPExcel->getActiveSheet()->getStyle($this->numberToColumn[$i].$lineNumber.':'.$convertTotalToString.$lineNumber)->applyFromArray(
466 'font' => array('bold' => true),
467 'alignment' => array('horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_LEFT),
469 'type' => \PHPExcel_Style_Fill::FILL_SOLID,
470 'color' => array('rgb'=>'aaaaaa')
475 foreach ($values as $key => $val)
477 if (is_a($val, 'DateTime'))
479 $this->_objPHPExcel->getActiveSheet()->getCell($this->numberToColumn[$i].$lineNumber)
482 $val->format(\Utility\Definitions\Locale::getDateTimeFormat())
483 ), \PHPExcel_Cell_DataType::TYPE_STRING
488 $output = $values[$key];
489 if (strpos($val, "<br />") || strpos($val, "<br/>") || strpos($val, "<br>"))
491 $this->_setColumnWrapping($this->numberToColumn[$i], true);
493 $output = str_replace(array("<br />", "<br/>", "<br>"), "\r", $output);
494 $output = str_replace(
495 array('/', '&', '�'),
496 array('-', 'and', ' degrees'),
499 $output = str_replace(
500 array("</u>", "<u>", "</b>", "<b>", "</i>", "<i>", "\\", "(", ")"),
504 /* $replacable = array("</u>", "<u>", "</b>", "<b>", "</i>", "<i>", "\\", "(", ")");
505 foreach($replacable as $val)
507 $output = str_replace(
508 array($val, '/', '&', '�'),
509 array('', ' - ', 'and', ' degrees'),
513 $type = is_numeric($output)
514 ? \PHPExcel_Cell_DataType::TYPE_NUMERIC
515 : \PHPExcel_Cell_DataType::TYPE_STRING;
516 $this->_objPHPExcel->getActiveSheet()->getCell($this->numberToColumn[$i].$lineNumber)
518 utf8_encode($output), $type
524 protected function _writeChart($lineNumber, $dataStart, $dataEnd)
526 $dataCount = $dataEnd - $dataStart;
527 $labelRow = $dataStart - 1;
528 $labelInd = array_search($this->_chart['LabelSource'], $this->_fields);
529 $labelInd = $this->numberToColumn[$labelInd];
531 $categories = array();
533 foreach ($this->_chart['DataSource'] as $field)
535 $fieldInd = array_search($field, $this->_fields);
536 $fieldInd = $this->numberToColumn[$fieldInd];
537 $labels[] = new \PHPExcel_Chart_DataSeriesValues(
539 'Worksheet!$' . $fieldInd . '$' . $labelRow,
542 $categories[] = new \PHPExcel_Chart_DataSeriesValues(
544 'Worksheet!$' . $labelInd . '$' . $dataStart . ':$' . $labelInd . '$' . $dataEnd,
547 $values[] = new \PHPExcel_Chart_DataSeriesValues(
549 'Worksheet!$' . $fieldInd . '$' . $dataStart . ':$' . $fieldInd . '$' . $dataEnd,
554 $sheet = $this->_objPHPExcel->getActiveSheet();
555 $series = new \PHPExcel_Chart_DataSeries(
556 $this->_chart['Type'], // plotType
557 $this->_chart['Grouping'], // plotGrouping
558 $this->_chart['Order'], // plotOrder
559 $labels, // plotLabel
560 $categories, // plotCategory
561 $values // plotValues
563 $series->setPlotDirection(\PHPExcel_Chart_DataSeries::DIRECTION_COL);
564 $plotarea = new \PHPExcel_Chart_PlotArea(null, array($series));
565 $legend = new \PHPExcel_Chart_Legend(\PHPExcel_Chart_Legend::POSITION_RIGHT, null, false);
566 $chart = new \PHPExcel_Chart(
567 $this->_chart['Name'], // name
570 $plotarea, // plotArea
571 true, // plotVisibleOnly
572 0, // displayBlanksAs
576 $chart->setTopLeftPosition('A' . $lineNumber);
577 $chart->setBottomRightPosition('J' . ($lineNumber + 15));
578 $sheet->addChart($chart);