2018-11-05 09:26:30 +08:00

418 lines
11 KiB
PHP

<?php
namespace Maatwebsite\Excel;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Events\AfterSheet;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Events\BeforeSheet;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Reader\Html;
use Maatwebsite\Excel\Concerns\WithCharts;
use Maatwebsite\Excel\Concerns\WithEvents;
use Illuminate\Contracts\Support\Arrayable;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithDrawings;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Maatwebsite\Excel\Concerns\WithCustomChunkSize;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use Maatwebsite\Excel\Exceptions\ConcernConflictException;
class Sheet
{
use DelegatedMacroable, HasEventBus;
/**
* @var int
*/
protected $chunkSize;
/**
* @var string
*/
protected $tmpPath;
/**
* @var object
*/
protected $exportable;
/**
* @var Worksheet
*/
private $worksheet;
/**
* @param Worksheet $worksheet
*/
public function __construct(Worksheet $worksheet)
{
$this->worksheet = $worksheet;
$this->chunkSize = config('excel.exports.chunk_size', 100);
$this->tmpPath = config('excel.exports.temp_path', sys_get_temp_dir());
}
/**
* @param object $sheetExport
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function open($sheetExport)
{
$this->exportable = $sheetExport;
if ($sheetExport instanceof WithEvents) {
$this->registerListeners($sheetExport->registerEvents());
}
$this->raise(new BeforeSheet($this, $this->exportable));
if ($sheetExport instanceof WithTitle) {
$this->worksheet->setTitle($sheetExport->title());
}
if (($sheetExport instanceof FromQuery || $sheetExport instanceof FromCollection) && $sheetExport instanceof FromView) {
throw ConcernConflictException::queryOrCollectionAndView();
}
if (!$sheetExport instanceof FromView && $sheetExport instanceof WithHeadings) {
if ($sheetExport instanceof WithCustomStartCell) {
$startCell = $sheetExport->startCell();
}
$this->append([$sheetExport->headings()], $startCell ?? null, $this->hasStrictNullComparison($sheetExport));
}
if ($sheetExport instanceof WithCharts) {
$this->addCharts($sheetExport->charts());
}
if ($sheetExport instanceof WithDrawings) {
$this->addDrawings($sheetExport->drawings());
}
}
/**
* @param object $sheetExport
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
public function export($sheetExport)
{
$this->open($sheetExport);
if ($sheetExport instanceof FromView) {
$this->fromView($sheetExport);
} else {
if ($sheetExport instanceof FromQuery) {
$this->fromQuery($sheetExport, $this->worksheet);
}
if ($sheetExport instanceof FromCollection) {
$this->fromCollection($sheetExport);
}
}
$this->close($sheetExport);
}
/**
* @param object $sheetExport
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function close($sheetExport)
{
$this->exportable = $sheetExport;
if ($sheetExport instanceof WithColumnFormatting) {
foreach ($sheetExport->columnFormats() as $column => $format) {
$this->formatColumn($column, $format);
}
}
if ($sheetExport instanceof ShouldAutoSize) {
$this->autoSize();
}
$this->raise(new AfterSheet($this, $this->exportable));
}
/**
* @param FromView $sheetExport
*
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
public function fromView(FromView $sheetExport)
{
$tempFile = $this->tempFile();
file_put_contents($tempFile, $sheetExport->view()->render());
$spreadsheet = $this->worksheet->getParent();
/** @var Html $reader */
$reader = IOFactory::createReader('Html');
// Insert content into the last sheet
$reader->setSheetIndex($spreadsheet->getSheetCount() - 1);
$reader->loadIntoExisting($tempFile, $spreadsheet);
}
/**
* @param FromQuery $sheetExport
* @param Worksheet $worksheet
*/
public function fromQuery(FromQuery $sheetExport, Worksheet $worksheet)
{
$sheetExport->query()->chunk($this->getChunkSize($sheetExport), function ($chunk) use ($sheetExport, $worksheet) {
foreach ($chunk as $row) {
$this->appendRow($row, $sheetExport);
}
});
}
/**
* @param FromCollection $sheetExport
*/
public function fromCollection(FromCollection $sheetExport)
{
$this->appendRows($sheetExport->collection()->all(), $sheetExport);
}
/**
* @param array $rows
* @param string|null $startCell
* @param bool $strictNullComparison
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function append(array $rows, string $startCell = null, bool $strictNullComparison = false)
{
if (!$startCell) {
$startCell = 'A1';
}
if ($this->hasRows()) {
$startCell = 'A' . ($this->worksheet->getHighestRow() + 1);
}
$this->worksheet->fromArray($rows, null, $startCell, $strictNullComparison);
}
/**
* @return void
*/
public function autoSize()
{
foreach ($this->buildColumnRange('A', $this->worksheet->getHighestDataColumn()) as $col) {
$this->worksheet->getColumnDimension($col)->setAutoSize(true);
}
}
/**
* @param string $column
* @param string $format
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function formatColumn(string $column, string $format)
{
$this->worksheet
->getStyle($column . '1:' . $column . $this->worksheet->getHighestRow())
->getNumberFormat()
->setFormatCode($format);
}
/**
* @param int $chunkSize
*
* @return Sheet
*/
public function chunkSize(int $chunkSize)
{
$this->chunkSize = $chunkSize;
return $this;
}
/**
* @return Worksheet
*/
public function getDelegate()
{
return $this->worksheet;
}
/**
* @param Chart|Chart[] $charts
*/
public function addCharts($charts)
{
$charts = \is_array($charts) ? $charts : [$charts];
foreach ($charts as $chart) {
$this->worksheet->addChart($chart);
}
}
/**
* @param BaseDrawing|BaseDrawing[] $drawings
*/
public function addDrawings($drawings)
{
$drawings = \is_array($drawings) ? $drawings : [$drawings];
foreach ($drawings as $drawing) {
$drawing->setWorksheet($this->worksheet);
}
}
/**
* @param string $concern
*
* @return string
*/
public function hasConcern(string $concern): string
{
return $this->exportable instanceof $concern;
}
/**
* @param iterable $rows
* @param object $sheetExport
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function appendRows($rows, $sheetExport)
{
$append = [];
foreach ($rows as $row) {
if ($sheetExport instanceof WithMapping) {
$row = $sheetExport->map($row);
}
$append[] = static::mapArraybleRow($row);
}
if ($sheetExport instanceof WithCustomStartCell) {
$startCell = $sheetExport->startCell();
}
$this->append($append, $startCell ?? null, $this->hasStrictNullComparison($sheetExport));
}
/**
* @param mixed $row
*
* @return array
*/
public static function mapArraybleRow($row): array
{
// When dealing with eloquent models, we'll skip the relations
// as we won't be able to display them anyway.
if (method_exists($row, 'attributesToArray')) {
return $row->attributesToArray();
}
if ($row instanceof Arrayable) {
return $row->toArray();
}
// Convert StdObjects to arrays
if (is_object($row)) {
return json_decode(json_encode($row), true);
}
return $row;
}
/**
* @param iterable $row
* @param object $sheetExport
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function appendRow($row, $sheetExport)
{
if ($sheetExport instanceof WithMapping) {
$row = $sheetExport->map($row);
}
$row = static::mapArraybleRow($row);
if ($sheetExport instanceof WithCustomStartCell) {
$startCell = $sheetExport->startCell();
}
if (isset($row[0]) && is_array($row[0])) {
$this->append($row, $startCell ?? null, $this->hasStrictNullComparison($sheetExport));
} else {
$this->append([$row], $startCell ?? null, $this->hasStrictNullComparison($sheetExport));
}
}
/**
* @param string $lower
* @param string $upper
*
* @return \Generator
*/
protected function buildColumnRange(string $lower, string $upper)
{
$upper++;
for ($i = $lower; $i !== $upper; $i++) {
yield $i;
}
}
/**
* @return string
*/
protected function tempFile(): string
{
return $this->tmpPath . DIRECTORY_SEPARATOR . 'laravel-excel-' . str_random(16);
}
/**
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @return bool
*/
private function hasRows(): bool
{
return $this->worksheet->cellExists('A1');
}
/**
* @param object $sheetExport
*
* @return bool
*/
private function hasStrictNullComparison($sheetExport): bool
{
return $sheetExport instanceof WithStrictNullComparison;
}
/**
* @param object|WithCustomChunkSize $export
*
* @return int
*/
private function getChunkSize($export): int
{
if ($export instanceof WithCustomChunkSize) {
return $export->chunkSize();
}
return $this->chunkSize;
}
}