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.
415 lines
10 KiB
415 lines
10 KiB
<?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 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; |
|
} |
|
}
|
|
|