Files
datatable.directory/porklib/Utils/SpreadsheetBuilder.php
T
2018-07-14 18:28:04 +02:00

416 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace MightyPork\Utils;
use Lang;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
// this requires "phpoffice/phpspreadsheet": "^1.3",
/**
* Abstraction above PhpOffice's spreadsheets with stable API.
* Fixing incompatibilities in one place will fix them in the entire application.
*/
class SpreadsheetBuilder
{
private $activeSheetNum = -1;
private $sheetCount = 0;
/** @var Spreadsheet Spreadsheet */
private $book = null;
/** @var Worksheet */
private $activeSheet = null;
/** @var string - document title */
private $title;
public function __construct($title, $subject = '', $creator='PhpOffice Spreadsheet')
{
$this->title = $title;
$this->book = new Spreadsheet();
$this->book->getProperties()
->setCreator($creator)
->setTitle($title)
->setSubject($subject)
->setDescription('Created ' . date('r'));
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Add a sheet and select it as active.
* Returns sheet index.
*
* @param $label - sheet label
* @return int - new sheet number, 0-based
*/
public function addSheet($label)
{
if ($this->activeSheetNum == -1) {
// PhpOffice workbook has one sheet by default,
// we require user to 'add' it before using it
$this->sheetCount = 1;
$this->activeSheet = $this->book->setActiveSheetIndex(0);
$this->activeSheetNum = 0;
}
else {
$this->sheetCount++;
$this->activeSheet = $this->book->addSheet(new Worksheet());
$this->activeSheetNum = $this->sheetCount-1;
}
$this->activeSheet->setTitle($label);
return $this->activeSheetNum;
}
/**
* Select a sheet for cell modifications
*
* @param int $number - sheet index, 0-based
*/
public function selectSheet($number)
{
$this->activeSheet = $this->book->setActiveSheetIndex($number);
$this->activeSheetNum = $number;
}
/**
* Set a cell value in the current sheet
*
* @param int $row - 0-based row
* @param int $column - 0-based column
* @param mixed $value - value to set
*/
public function setValue($row, $column, $value)
{
$this->activeSheet->setCellValueByColumnAndRow($column+1, $row+1, $value);
}
/**
* Set a row of values
*
* @param int $row - 0-based row
* @param int $startcol - first column to fill
* @param array $values - array of values to write into the row
*/
public function setRow($row, $startcol, $values)
{
$i = 0;
foreach ($values as $value) {
$this->setValue($row, $startcol + $i, $value);
$i++;
}
}
/**
* Set a row of values
*
* @param int $startrow - 0-based start row
* @param int $startcol - 0-based start column
* @param array $values - array of row vectors ([ [first,row], [second,row], ...]
*/
public function setGrid($startrow, $startcol, $values)
{
foreach ($values as $row => $row_values) {
$this->setRow($startrow + $row, $startcol, $row_values);
}
}
/**
* Make a cell a given style
*
* @param int $row - 0-based row
* @param int $column - 0-based column
*/
private function setStyle($row, $column, $array)
{
$coord = Coordinate::stringFromColumnIndex($column+1) . ($row+1);
$this->activeSheet->getStyle($coord)
->applyFromArray($array);
}
/**
* Make an area of cells a given style
*
* @param $startrow - 0-based first row
* @param $startcol - 0-based first column
* @param $rows - number of rows
* @param $cols - number of columns
*/
private function setStyleArea($startrow, $startcol, $rows, $cols, $array)
{
$coord = Coordinate::stringFromColumnIndex($startcol+1) . ($startrow+1) .
':' . Coordinate::stringFromColumnIndex($startcol+$cols) . ($startrow+$rows);
$this->activeSheet->getStyle($coord)
->applyFromArray($array);
}
/**
* Make a cell bold
*
* @param int $row - 0-based row
* @param int $column - 0-based column
*/
public function setBold($row, $column)
{
$this->setStyle($row, $column, ['font' => ['bold' => 'true']]);
}
/**
* Make an area of cells bold
*
* @param $startrow - 0-based first row
* @param $startcol - 0-based first column
* @param $rows - number of rows
* @param $cols - number of columns
*/
public function setBoldArea($startrow, $startcol, $rows, $cols)
{
$this->setStyleArea($startrow, $startcol, $rows, $cols, ['font' => ['bold' => 'true']]);
}
/**
* Make a cell italic
*
* @param int $row - 0-based row
* @param int $column - 0-based column
*/
public function setItalic($row, $column)
{
$this->setStyle($row, $column, ['font' => ['italic' => 'true']]);
}
/**
* Make an area of cells italic
*
* @param $startrow - 0-based first row
* @param $startcol - 0-based first column
* @param $rows - number of rows
* @param $cols - number of columns
*/
public function setItalicArea($startrow, $startcol, $rows, $cols)
{
$this->setStyleArea($startrow, $startcol, $rows, $cols, ['font' => ['italic' => 'true']]);
}
private function convertColor($color)
{
if ($color[0] == '#') $rgb = substr($color, 1);
else {
$map = [
'black' => '000000',
'gray' => '808080',
'silver' => 'C0C0C0',
'white' => 'FFFFFF',
'maroon' => '800000',
'red' => 'FF0000',
'olive' => '808000',
'yellow' => 'FFFF00',
'green' => '008000',
'lime' => '00FF00',
'teal' => '008080',
'aqua' => '00FFFF',
'navy' => '000080',
'blue' => '0000FF',
'purple' => '800080',
'fuchsia' => 'FF00FF',
];
if (isset($map[$color])) {
$rgb = $map[$color];
} else {
throw new \LogicException("Bad color format $color");
}
}
return ['rgb' => $rgb];
}
/**
* Set cell colors
*
* @param int $row - 0-based row
* @param int $column - 0-based column
* @param string $text - foreground
* @param string $background - background
*/
public function setColors($row, $column, $text = null, $background = null)
{
$this->setColorsArea($row, $column, 1, 1, $text, $background);
}
/**
* Set colors of an area of cells
*
* @param $startrow - 0-based first row
* @param $startcol - 0-based first column
* @param $rows - number of rows
* @param $cols - number of columns
* @param string $text - foreground
* @param string $background - background
*/
public function setColorsArea($startrow, $startcol, $rows, $cols, $text = null, $background = null)
{
$ar = [];
if ($text !== null) {
$ar['font'] = [
'color' => $this->convertColor($text),
];
}
if ($background !== null) {
$ar['fill'] = [
'fillType' => Fill::FILL_SOLID,
'color' => $this->convertColor($background),
];
}
$this->setStyleArea($startrow, $startcol, $rows, $cols, $ar);
}
/**
* Merge cells
*
* @param $startrow - 0-based first row
* @param $startcol - 0-based first column
* @param $rows - number of rows
* @param $cols - number of columns
*/
public function mergeCells($startrow, $startcol, $rows, $cols)
{
$coord = Coordinate::stringFromColumnIndex($startcol+1) . ($startrow+1) .
':' . Coordinate::stringFromColumnIndex($startcol+$cols) . ($startrow+$rows);
$this->activeSheet->mergeCells($coord);
}
/**
* Merge row cells
*
* @param $startrow - 0-based first row
* @param $startcol - 0-based first column
* @param $rows - number of rows
* @param $cols - number of columns
*/
public function mergeRowCells($startrow, $startcol, $cols)
{
$this->mergeCells($startrow, $startcol, 1, $cols);
}
/**
* Set a column's width
*
* @param int $column - 0-based column number
* @param int $width - width in pixels
*/
public function setColumnWidth($column, $width)
{
$this->activeSheet->getColumnDimensionByColumn($column+1)->setWidth($width);
}
/**
* Prepare writer and resolve mime type for export
*
* @param string $format - one of : xls, xlsx, csv, ods
* @return array (mime, writer)
*/
private function prepareExport($format = 'xlsx')
{
switch ($format) {
case 'xls':
$writerName = 'Xls';
$mimeType = 'application/vnd.ms-excel';
break;
case 'xlsx':
$writerName = 'Xlsx';
$mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
break;
case 'ods':
$writerName = 'Ods';
$mimeType = 'application/vnd.oasis.opendocument.spreadsheet';
break;
default:
case 'csv':
$writerName = 'Csv';
$mimeType = 'text/csv';
break;
}
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$this->book->setActiveSheetIndex(0);
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($this->book, $writerName);
return [$mimeType, $writer];
}
/**
* Export and send to browser as a response for user to download
*
* @param string $base_filename - filename without suffix
* @param string $format - one of : xls, xlsx, csv, ods
*/
public function exportToBrowser($base_filename, $format = 'xlsx')
{
list($mimeType, $writer) = $this->prepareExport($format);
/** @var IWriter $writer */
$base_filename .= '.' . $format;
ob_end_clean();
// Redirect output to a clients web browser (Xlsx)
header("Content-Type: $mimeType; charset=utf-8");
header("Content-Disposition: attachment;filename=\"$base_filename\"");
header('Content-Language: ' . Lang::locale());
// Cache headers
header('Cache-Control: max-age=0');
// IE9
header('Cache-Control: max-age=1');
// Other IE headers
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header('Pragma: no-cache'); // HTTP/1.0
$writer->save('php://output');
exit;
}
/**
* Export and send to browser as a response for user to download
*
* @param string $base_filename - filename without suffix
* @param string $format - one of : xls, xlsx, csv, ods
* @return string - the mime type
*/
public function exportToFile($base_filename, $format = 'xlsx', $path='/tmp')
{
list($mimeType, $writer) = $this->prepareExport($format);
/** @var IWriter $writer */
$writer->save($path . DIRECTORY_SEPARATOR . "$base_filename.$format");
return $mimeType;
}
}