datatable.directory codebase https://datatable.directory/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
datatable.directory/porklib/Utils/SpreadsheetBuilder.php

416 lines
10 KiB

6 years ago
<?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;
6 years ago
// this requires "phpoffice/phpspreadsheet": "^1.3",
6 years ago
/**
* 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 client’s 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;
}
}