update inbox list

This commit is contained in:
manhlab
2021-04-07 19:25:18 -04:00
parent fda7245f7c
commit 436de2efd6
8576 changed files with 1013325 additions and 3 deletions

82
vendor/maatwebsite/excel/src/Cell.php vendored Normal file
View File

@@ -0,0 +1,82 @@
<?php
namespace Maatwebsite\Excel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Cell\Cell as SpreadsheetCell;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Cell
{
use DelegatedMacroable;
/**
* @var SpreadsheetCell
*/
private $cell;
/**
* @param SpreadsheetCell $cell
*/
public function __construct(SpreadsheetCell $cell)
{
$this->cell = $cell;
}
/**
* @param Worksheet $worksheet
* @param string $coordinate
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @return Cell
*/
public static function make(Worksheet $worksheet, string $coordinate)
{
return new static($worksheet->getCell($coordinate));
}
/**
* @return SpreadsheetCell
*/
public function getDelegate(): SpreadsheetCell
{
return $this->cell;
}
/**
* @param null $nullValue
* @param bool $calculateFormulas
* @param bool $formatData
*
* @return mixed
*/
public function getValue($nullValue = null, $calculateFormulas = false, $formatData = true)
{
$value = $nullValue;
if ($this->cell->getValue() !== null) {
if ($this->cell->getValue() instanceof RichText) {
$value = $this->cell->getValue()->getPlainText();
} elseif ($calculateFormulas) {
try {
$value = $this->cell->getCalculatedValue();
} catch (Exception $e) {
$value = $this->cell->getOldCalculatedValue();
}
} else {
$value = $this->cell->getValue();
}
if ($formatData) {
$style = $this->cell->getWorksheet()->getParent()->getCellXfByIndex($this->cell->getXfIndex());
$value = NumberFormat::toFormattedString(
$value,
($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : NumberFormat::FORMAT_GENERAL
);
}
}
return $value;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Maatwebsite\Excel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithLimit;
use Maatwebsite\Excel\Concerns\WithProgressBar;
use Maatwebsite\Excel\Events\BeforeImport;
use Maatwebsite\Excel\Files\TemporaryFile;
use Maatwebsite\Excel\Imports\HeadingRowExtractor;
use Maatwebsite\Excel\Jobs\AfterImportJob;
use Maatwebsite\Excel\Jobs\QueueImport;
use Maatwebsite\Excel\Jobs\ReadChunk;
use Throwable;
class ChunkReader
{
/**
* @param WithChunkReading $import
* @param Reader $reader
* @param TemporaryFile $temporaryFile
*
* @return \Illuminate\Foundation\Bus\PendingDispatch|null
*/
public function read(WithChunkReading $import, Reader $reader, TemporaryFile $temporaryFile)
{
if ($import instanceof WithEvents && isset($import->registerEvents()[BeforeImport::class])) {
$reader->beforeImport($import);
}
$chunkSize = $import->chunkSize();
$totalRows = $reader->getTotalRows();
$worksheets = $reader->getWorksheets($import);
if ($import instanceof WithProgressBar) {
$import->getConsoleOutput()->progressStart(array_sum($totalRows));
}
$jobs = new Collection();
foreach ($worksheets as $name => $sheetImport) {
$startRow = HeadingRowExtractor::determineStartRow($sheetImport);
$totalRows[$name] = $sheetImport instanceof WithLimit ? $sheetImport->limit() : $totalRows[$name];
for ($currentRow = $startRow; $currentRow <= $totalRows[$name]; $currentRow += $chunkSize) {
$jobs->push(new ReadChunk(
$import,
$reader->getPhpSpreadsheetReader(),
$temporaryFile,
$name,
$sheetImport,
$currentRow,
$chunkSize
));
}
}
$jobs->push(new AfterImportJob($import, $reader));
if ($import instanceof ShouldQueue) {
return QueueImport::withChain($jobs->toArray())->dispatch($import);
}
$jobs->each(function ($job) {
try {
dispatch_now($job);
} catch (Throwable $e) {
if (method_exists($job, 'failed')) {
$job->failed($e);
}
throw $e;
}
});
if ($import instanceof WithProgressBar) {
$import->getConsoleOutput()->progressFinish();
}
unset($jobs);
return null;
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Foundation\Bus\PendingDispatch;
use Maatwebsite\Excel\Exceptions\NoFilenameGivenException;
use Maatwebsite\Excel\Exceptions\NoFilePathGivenException;
use Maatwebsite\Excel\Exporter;
trait Exportable
{
/**
* @param string $fileName
* @param string|null $writerType
* @param array $headers
*
* @throws NoFilenameGivenException
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download(string $fileName = null, string $writerType = null, array $headers = null)
{
$headers = $headers ?? $this->headers ?? [];
$fileName = $fileName ?? $this->fileName ?? null;
$writerType = $writerType ?? $this->writerType ?? null;
if (null === $fileName) {
throw new NoFilenameGivenException();
}
return $this->getExporter()->download($this, $fileName, $writerType, $headers);
}
/**
* @param string $filePath
* @param string|null $disk
* @param string|null $writerType
* @param mixed $diskOptions
*
* @throws NoFilePathGivenException
* @return bool|PendingDispatch
*/
public function store(string $filePath = null, string $disk = null, string $writerType = null, $diskOptions = [])
{
$filePath = $filePath ?? $this->filePath ?? null;
if (null === $filePath) {
throw NoFilePathGivenException::export();
}
return $this->getExporter()->store(
$this,
$filePath,
$disk ?? $this->disk ?? null,
$writerType ?? $this->writerType ?? null,
$diskOptions ?? $this->diskOptions ?? []
);
}
/**
* @param string|null $filePath
* @param string|null $disk
* @param string|null $writerType
* @param mixed $diskOptions
*
* @throws NoFilePathGivenException
* @return PendingDispatch
*/
public function queue(string $filePath = null, string $disk = null, string $writerType = null, $diskOptions = [])
{
$filePath = $filePath ?? $this->filePath ?? null;
if (null === $filePath) {
throw NoFilePathGivenException::export();
}
return $this->getExporter()->queue(
$this,
$filePath,
$disk ?? $this->disk ?? null,
$writerType ?? $this->writerType ?? null,
$diskOptions ?? $this->diskOptions ?? []
);
}
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
*
* @throws NoFilenameGivenException
* @return \Illuminate\Http\Response
*/
public function toResponse($request)
{
return $this->download();
}
/**
* @return Exporter
*/
private function getExporter(): Exporter
{
return app(Exporter::class);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface FromArray
{
/**
* @return array
*/
public function array(): array;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Support\Collection;
interface FromCollection
{
/**
* @return Collection
*/
public function collection();
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Generator;
interface FromGenerator
{
/**
* @return Generator
*/
public function generator(): Generator;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Iterator;
interface FromIterator
{
/**
* @return Iterator
*/
public function iterator(): Iterator;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Database\Query\Builder;
interface FromQuery
{
/**
* @return Builder
*/
public function query();
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Contracts\View\View;
interface FromView
{
/**
* @return View
*/
public function view(): View;
}

View File

@@ -0,0 +1,149 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Console\OutputStyle;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Support\Collection;
use InvalidArgumentException;
use Maatwebsite\Excel\Exceptions\NoFilePathGivenException;
use Maatwebsite\Excel\Importer;
use Symfony\Component\HttpFoundation\File\UploadedFile;
trait Importable
{
/**
* @var OutputStyle|null
*/
protected $output;
/**
* @param string|UploadedFile|null $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @throws NoFilePathGivenException
* @return Importer|PendingDispatch
*/
public function import($filePath = null, string $disk = null, string $readerType = null)
{
$filePath = $this->getFilePath($filePath);
return $this->getImporter()->import(
$this,
$filePath,
$disk ?? $this->disk ?? null,
$readerType ?? $this->readerType ?? null
);
}
/**
* @param string|UploadedFile|null $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @throws NoFilePathGivenException
* @return array
*/
public function toArray($filePath = null, string $disk = null, string $readerType = null): array
{
$filePath = $this->getFilePath($filePath);
return $this->getImporter()->toArray(
$this,
$filePath,
$disk ?? $this->disk ?? null,
$readerType ?? $this->readerType ?? null
);
}
/**
* @param string|UploadedFile|null $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @throws NoFilePathGivenException
* @return Collection
*/
public function toCollection($filePath = null, string $disk = null, string $readerType = null): Collection
{
$filePath = $this->getFilePath($filePath);
return $this->getImporter()->toCollection(
$this,
$filePath,
$disk ?? $this->disk ?? null,
$readerType ?? $this->readerType ?? null
);
}
/**
* @param string|UploadedFile|null $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @throws NoFilePathGivenException
* @throws InvalidArgumentException
* @return PendingDispatch
*/
public function queue($filePath = null, string $disk = null, string $readerType = null)
{
if (!$this instanceof ShouldQueue) {
throw new InvalidArgumentException('Importable should implement ShouldQueue to be queued.');
}
return $this->import($filePath, $disk, $readerType);
}
/**
* @param OutputStyle $output
*
* @return $this
*/
public function withOutput(OutputStyle $output)
{
$this->output = $output;
return $this;
}
/**
* @return OutputStyle
*/
public function getConsoleOutput(): OutputStyle
{
if (!$this->output instanceof OutputStyle) {
throw new InvalidArgumentException(
'Importable has no OutputStyle. Declare one by using ->withOutput($this->output).'
);
}
return $this->output;
}
/**
* @param UploadedFile|string|null $filePath
*
* @throws NoFilePathGivenException
* @return UploadedFile|string
*/
private function getFilePath($filePath = null)
{
$filePath = $filePath ?? $this->filePath ?? null;
if (null === $filePath) {
throw NoFilePathGivenException::import();
}
return $filePath;
}
/**
* @return Importer
*/
private function getImporter(): Importer
{
return app(Importer::class);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Support\Arr;
trait MapsCsvSettings
{
/**
* @var string
*/
protected static $delimiter = ',';
/**
* @var string
*/
protected static $enclosure = '"';
/**
* @var string
*/
protected static $lineEnding = PHP_EOL;
/**
* @var bool
*/
protected static $useBom = false;
/**
* @var bool
*/
protected static $includeSeparatorLine = false;
/**
* @var bool
*/
protected static $excelCompatibility = false;
/**
* @var string
*/
protected static $escapeCharacter = '\\';
/**
* @var bool
*/
protected static $contiguous = false;
/**
* @var string
*/
protected static $inputEncoding = 'UTF-8';
/**
* @param array $config
*/
public static function applyCsvSettings(array $config)
{
static::$delimiter = Arr::get($config, 'delimiter', static::$delimiter);
static::$enclosure = Arr::get($config, 'enclosure', static::$enclosure);
static::$lineEnding = Arr::get($config, 'line_ending', static::$lineEnding);
static::$useBom = Arr::get($config, 'use_bom', static::$useBom);
static::$includeSeparatorLine = Arr::get($config, 'include_separator_line', static::$includeSeparatorLine);
static::$excelCompatibility = Arr::get($config, 'excel_compatibility', static::$excelCompatibility);
static::$escapeCharacter = Arr::get($config, 'escape_character', static::$escapeCharacter);
static::$contiguous = Arr::get($config, 'contiguous', static::$contiguous);
static::$inputEncoding = Arr::get($config, 'input_encoding', static::$inputEncoding);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Maatwebsite\Excel\Row;
interface OnEachRow
{
/**
* @param Row $row
*/
public function onRow(Row $row);
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Maatwebsite\Excel\Events\AfterImport;
use Maatwebsite\Excel\Events\AfterSheet;
use Maatwebsite\Excel\Events\BeforeExport;
use Maatwebsite\Excel\Events\BeforeImport;
use Maatwebsite\Excel\Events\BeforeSheet;
use Maatwebsite\Excel\Events\BeforeWriting;
use Maatwebsite\Excel\Events\ImportFailed;
trait RegistersEventListeners
{
/**
* @return array
*/
public function registerEvents(): array
{
$listeners = [];
if (method_exists($this, 'beforeExport')) {
$listeners[BeforeExport::class] = [static::class, 'beforeExport'];
}
if (method_exists($this, 'beforeWriting')) {
$listeners[BeforeWriting::class] = [static::class, 'beforeWriting'];
}
if (method_exists($this, 'beforeImport')) {
$listeners[BeforeImport::class] = [static::class, 'beforeImport'];
}
if (method_exists($this, 'afterImport')) {
$listeners[AfterImport::class] = [static::class, 'afterImport'];
}
if (method_exists($this, 'importFailed')) {
$listeners[ImportFailed::class] = [static::class, 'importFailed'];
}
if (method_exists($this, 'beforeSheet')) {
$listeners[BeforeSheet::class] = [static::class, 'beforeSheet'];
}
if (method_exists($this, 'afterSheet')) {
$listeners[AfterSheet::class] = [static::class, 'afterSheet'];
}
return $listeners;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface ShouldAutoSize
{
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Validators\Failure;
use Throwable;
trait SkipsErrors
{
/**
* @var Failure[]
*/
protected $errors = [];
/**
* @param Throwable $e
*/
public function onError(Throwable $e)
{
$this->errors[] = $e;
}
/**
* @return Throwable[]|Collection
*/
public function errors(): Collection
{
return new Collection($this->errors);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Validators\Failure;
trait SkipsFailures
{
/**
* @var Failure[]
*/
protected $failures = [];
/**
* @param Failure ...$failures
*/
public function onFailure(Failure ...$failures)
{
$this->failures = array_merge($this->failures, $failures);
}
/**
* @return Failure[]|Collection
*/
public function failures(): Collection
{
return new Collection($this->failures);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Throwable;
interface SkipsOnError
{
/**
* @param Throwable $e
*/
public function onError(Throwable $e);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Maatwebsite\Excel\Validators\Failure;
interface SkipsOnFailure
{
/**
* @param Failure[] $failures
*/
public function onFailure(Failure ...$failures);
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface SkipsUnknownSheets
{
/**
* @param string|int $sheetName
*/
public function onUnknownSheet($sheetName);
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface ToArray
{
/**
* @param array $array
*/
public function array(array $array);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Support\Collection;
interface ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection);
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Database\Eloquent\Model;
interface ToModel
{
/**
* @param array $row
*
* @return Model|Model[]|null
*/
public function model(array $row);
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithBatchInserts
{
/**
* @return int
*/
public function batchSize(): int;
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithCalculatedFormulas
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
interface WithCharts
{
/**
* @return Chart|Chart[]
*/
public function charts();
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithChunkReading
{
/**
* @return int
*/
public function chunkSize(): int;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithColumnFormatting
{
/**
* @return array
*/
public function columnFormats(): array;
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Maatwebsite\Excel\Concerns;
trait WithConditionalSheets
{
/**
* @var array
*/
protected $conditionallySelectedSheets = [];
/**
* @param string|array $sheets
*
* @return $this
*/
public function onlySheets($sheets)
{
$this->conditionallySelectedSheets = is_array($sheets) ? $sheets : func_get_args();
return $this;
}
/**
* @return array
*/
public function sheets(): array
{
return \array_filter($this->conditionalSheets(), function ($name) {
return \in_array($name, $this->conditionallySelectedSheets, false);
}, ARRAY_FILTER_USE_KEY);
}
/**
* @return array
*/
abstract public function conditionalSheets(): array;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithCustomChunkSize
{
/**
* @return int
*/
public function chunkSize(): int;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithCustomCsvSettings
{
/**
* @return array
*/
public function getCsvSettings(): array;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithCustomQuerySize
{
/**
* Queued exportables are processed in chunks; each chunk being a job pushed to the queue by the QueuedWriter.
* In case of exportables that implement the FromQuery concern, the number of jobs is calculated by dividing the $query->count() by the chunk size.
* Depending on the implementation of the query() method (eg. When using a groupBy clause), this calculation might not be correct.
*
* When this is the case, you should use this method to provide a custom calculation of the query size.
*
* @return int
*/
public function querySize(): int;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithCustomStartCell
{
/**
* @return string
*/
public function startCell(): string;
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
interface WithCustomValueBinder extends IValueBinder
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
interface WithDrawings
{
/**
* @return BaseDrawing|BaseDrawing[]
*/
public function drawings();
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithEvents
{
/**
* @return array
*/
public function registerEvents(): array;
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithHeadingRow
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithHeadings
{
/**
* @return array
*/
public function headings(): array;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithLimit
{
/**
* @return int
*/
public function limit(): int;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithMappedCells
{
/**
* @return array
*/
public function mapping(): array;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithMapping
{
/**
* @param mixed $row
*
* @return array
*/
public function map($row): array;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithMultipleSheets
{
/**
* @return array
*/
public function sheets(): array;
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithPreCalculateFormulas
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Maatwebsite\Excel\Concerns;
use Illuminate\Console\OutputStyle;
interface WithProgressBar
{
/**
* @return OutputStyle
*/
public function getConsoleOutput(): OutputStyle;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithStartRow
{
/**
* @return int
*/
public function startRow(): int;
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithStrictNullComparison
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithTitle
{
/**
* @return string
*/
public function title(): string;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Maatwebsite\Excel\Concerns;
interface WithValidation
{
/**
* @return array
*/
public function rules(): array;
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Maatwebsite\Excel\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Input\InputOption;
class ExportMakeCommand extends GeneratorCommand
{
use WithModelStub;
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:export';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new export class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Export';
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
if ($this->option('model') && $this->option('query')) {
$stub = '/stubs/export.query-model.stub';
} elseif ($this->option('model')) {
$stub = '/stubs/export.model.stub';
} elseif ($this->option('query')) {
$stub = '/stubs/export.query.stub';
}
$stub = $stub ?? '/stubs/export.plain.stub';
return __DIR__ . $stub;
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
*
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace . '\Exports';
}
/**
* Build the class with the given name.
* Remove the base controller import if we are already in base namespace.
*
* @param string $name
*
* @return string
*/
protected function buildClass($name)
{
$replace = [];
if ($this->option('model')) {
$replace = $this->buildModelReplacements($replace);
}
return str_replace(
array_keys($replace), array_values($replace), parent::buildClass($name)
);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['model', 'm', InputOption::VALUE_OPTIONAL, 'Generate an export for the given model.'],
['query', '', InputOption::VALUE_NONE, 'Generate an export for a query.'],
];
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Maatwebsite\Excel\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Input\InputOption;
class ImportMakeCommand extends GeneratorCommand
{
use WithModelStub;
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:import';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new import class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Import';
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
if ($this->option('model')) {
$stub = '/stubs/import.model.stub';
}
$stub = $stub ?? '/stubs/import.collection.stub';
return __DIR__ . $stub;
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
*
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace . '\Imports';
}
/**
* Build the class with the given name.
* Remove the base controller import if we are already in base namespace.
*
* @param string $name
*
* @return string
*/
protected function buildClass($name)
{
$replace = [];
if ($this->option('model')) {
$replace = $this->buildModelReplacements($replace);
}
return str_replace(
array_keys($replace), array_values($replace), parent::buildClass($name)
);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['model', 'm', InputOption::VALUE_OPTIONAL, 'Generate an import for the given model.'],
['query', '', InputOption::VALUE_NONE, 'Generate an import for a query.'],
];
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Maatwebsite\Excel\Console;
use Illuminate\Support\Str;
use InvalidArgumentException;
trait WithModelStub
{
/**
* Build the model replacement values.
*
* @param array $replace
*
* @return array
*/
protected function buildModelReplacements(array $replace): array
{
$modelClass = $this->parseModel($this->option('model'));
return array_merge($replace, [
'DummyFullModelClass' => $modelClass,
'DummyModelClass' => class_basename($modelClass),
]);
}
/**
* Get the fully-qualified model class name.
*
* @param string $model
*
* @return string
*/
protected function parseModel($model): string
{
if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
throw new InvalidArgumentException('Model name contains invalid characters.');
}
$model = trim(str_replace('/', '\\', $model), '\\');
if (!Str::startsWith($model, $rootNamespace = $this->laravel->getNamespace())) {
$model = $rootNamespace . $model;
}
return $model;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace DummyNamespace;
use DummyFullModelClass;
use Maatwebsite\Excel\Concerns\FromCollection;
class DummyClass implements FromCollection
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return DummyModelClass::all();
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace DummyNamespace;
use Maatwebsite\Excel\Concerns\FromCollection;
class DummyClass implements FromCollection
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
//
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace DummyNamespace;
use DummyFullModelClass;
use Maatwebsite\Excel\Concerns\FromQuery;
class DummyClass implements FromQuery
{
/**
* @return \Illuminate\Database\Query\Builder
*/
public function query()
{
return DummyModelClass::query();
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace DummyNamespace;
use Maatwebsite\Excel\Concerns\FromQuery;
class DummyClass implements FromQuery
{
/**
* @return \Illuminate\Database\Query\Builder
*/
public function query()
{
//
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace DummyNamespace;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
class DummyClass implements ToCollection
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
//
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace DummyNamespace;
use DummyFullModelClass;
use Maatwebsite\Excel\Concerns\ToModel;
class DummyClass implements ToModel
{
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new DummyModelClass([
//
]);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Maatwebsite\Excel;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder as PhpSpreadsheetDefaultValueBinder;
class DefaultValueBinder extends PhpSpreadsheetDefaultValueBinder
{
/**
* @param Cell $cell Cell to bind value to
* @param mixed $value Value to bind in cell
*
* @return bool
*/
public function bindValue(Cell $cell, $value)
{
if (is_array($value)) {
$value = \json_encode($value);
}
return parent::bindValue($cell, $value);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Maatwebsite\Excel;
use Illuminate\Support\Traits\Macroable;
trait DelegatedMacroable
{
use Macroable {
__call as __callMacro;
}
/**
* Dynamically handle calls to the class.
*
* @param string $method
* @param array $parameters
*
* @return mixed
*/
public function __call($method, $parameters)
{
if (method_exists($this->getDelegate(), $method)) {
return call_user_func_array([$this->getDelegate(), $method], $parameters);
}
array_unshift($parameters, $this);
return $this->__callMacro($method, $parameters);
}
/**
* @return object
*/
abstract public function getDelegate();
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Events;
use Maatwebsite\Excel\Reader;
class AfterImport extends Event
{
/**
* @var Reader
*/
public $reader;
/**
* @var object
*/
private $importable;
/**
* @param Reader $reader
* @param object $importable
*/
public function __construct(Reader $reader, $importable)
{
$this->reader = $reader;
$this->importable = $importable;
}
/**
* @return Reader
*/
public function getReader(): Reader
{
return $this->reader;
}
/**
* @return object
*/
public function getConcernable()
{
return $this->importable;
}
/**
* @return mixed
*/
public function getDelegate()
{
return $this->reader;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Events;
use Maatwebsite\Excel\Sheet;
class AfterSheet extends Event
{
/**
* @var Sheet
*/
public $sheet;
/**
* @var object
*/
private $exportable;
/**
* @param Sheet $sheet
* @param object $exportable
*/
public function __construct(Sheet $sheet, $exportable)
{
$this->sheet = $sheet;
$this->exportable = $exportable;
}
/**
* @return Sheet
*/
public function getSheet(): Sheet
{
return $this->sheet;
}
/**
* @return object
*/
public function getConcernable()
{
return $this->exportable;
}
/**
* @return mixed
*/
public function getDelegate()
{
return $this->sheet;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Events;
use Maatwebsite\Excel\Writer;
class BeforeExport extends Event
{
/**
* @var Writer
*/
public $writer;
/**
* @var object
*/
private $exportable;
/**
* @param Writer $writer
* @param object $exportable
*/
public function __construct(Writer $writer, $exportable)
{
$this->writer = $writer;
$this->exportable = $exportable;
}
/**
* @return Writer
*/
public function getWriter(): Writer
{
return $this->writer;
}
/**
* @return object
*/
public function getConcernable()
{
return $this->exportable;
}
/**
* @return mixed
*/
public function getDelegate()
{
return $this->writer;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Events;
use Maatwebsite\Excel\Reader;
class BeforeImport extends Event
{
/**
* @var Reader
*/
public $reader;
/**
* @var object
*/
private $importable;
/**
* @param Reader $reader
* @param object $importable
*/
public function __construct(Reader $reader, $importable)
{
$this->reader = $reader;
$this->importable = $importable;
}
/**
* @return Reader
*/
public function getReader(): Reader
{
return $this->reader;
}
/**
* @return object
*/
public function getConcernable()
{
return $this->importable;
}
/**
* @return mixed
*/
public function getDelegate()
{
return $this->reader;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Events;
use Maatwebsite\Excel\Sheet;
class BeforeSheet extends Event
{
/**
* @var Sheet
*/
public $sheet;
/**
* @var object
*/
private $exportable;
/**
* @param Sheet $sheet
* @param object $exportable
*/
public function __construct(Sheet $sheet, $exportable)
{
$this->sheet = $sheet;
$this->exportable = $exportable;
}
/**
* @return Sheet
*/
public function getSheet(): Sheet
{
return $this->sheet;
}
/**
* @return object
*/
public function getConcernable()
{
return $this->exportable;
}
/**
* @return mixed
*/
public function getDelegate()
{
return $this->sheet;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Maatwebsite\Excel\Events;
use Maatwebsite\Excel\Writer;
class BeforeWriting extends Event
{
/**
* @var Writer
*/
public $writer;
/**
* @var object
*/
private $exportable;
/**
* @param Writer $writer
* @param object $exportable
*/
public function __construct(Writer $writer, $exportable)
{
$this->writer = $writer;
$this->exportable = $exportable;
}
/**
* @return Writer
*/
public function getWriter(): Writer
{
return $this->writer;
}
/**
* @return object
*/
public function getConcernable()
{
return $this->exportable;
}
/**
* @return mixed
*/
public function getDelegate()
{
return $this->writer;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Maatwebsite\Excel\Events;
abstract class Event
{
/**
* @return object
*/
abstract public function getConcernable();
/**
* @return mixed
*/
abstract public function getDelegate();
/**
* @param string $concern
*
* @return bool
*/
public function appliesToConcern(string $concern): bool
{
return $this->getConcernable() instanceof $concern;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Maatwebsite\Excel\Events;
use Throwable;
class ImportFailed
{
/**
* @var Throwable
*/
public $e;
/**
* @param Throwable $e
*/
public function __construct(Throwable $e)
{
$this->e = $e;
}
/**
* @return Throwable
*/
public function getException(): Throwable
{
return $this->e;
}
}

197
vendor/maatwebsite/excel/src/Excel.php vendored Normal file
View File

@@ -0,0 +1,197 @@
<?php
namespace Maatwebsite\Excel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Files\Filesystem;
use Maatwebsite\Excel\Files\TemporaryFile;
use Maatwebsite\Excel\Helpers\FileTypeDetector;
class Excel implements Exporter, Importer
{
use RegistersCustomConcerns;
const XLSX = 'Xlsx';
const CSV = 'Csv';
const TSV = 'Csv';
const ODS = 'Ods';
const XLS = 'Xls';
const SLK = 'Slk';
const XML = 'Xml';
const GNUMERIC = 'Gnumeric';
const HTML = 'Html';
const MPDF = 'Mpdf';
const DOMPDF = 'Dompdf';
const TCPDF = 'Tcpdf';
/**
* @var Writer
*/
protected $writer;
/**
* @var QueuedWriter
*/
protected $queuedWriter;
/**
* @var Filesystem
*/
protected $filesystem;
/**
* @var Reader
*/
private $reader;
/**
* @param Writer $writer
* @param QueuedWriter $queuedWriter
* @param Reader $reader
* @param Filesystem $filesystem
*/
public function __construct(
Writer $writer,
QueuedWriter $queuedWriter,
Reader $reader,
Filesystem $filesystem
) {
$this->writer = $writer;
$this->reader = $reader;
$this->filesystem = $filesystem;
$this->queuedWriter = $queuedWriter;
}
/**
* {@inheritdoc}
*/
public function download($export, string $fileName, string $writerType = null, array $headers = [])
{
return response()->download(
$this->export($export, $fileName, $writerType)->getLocalPath(),
$fileName,
$headers
)->deleteFileAfterSend(true);
}
/**
* {@inheritdoc}
*/
public function store($export, string $filePath, string $diskName = null, string $writerType = null, $diskOptions = [])
{
if ($export instanceof ShouldQueue) {
return $this->queue($export, $filePath, $diskName, $writerType, $diskOptions);
}
$temporaryFile = $this->export($export, $filePath, $writerType);
$exported = $this->filesystem->disk($diskName, $diskOptions)->copy(
$temporaryFile,
$filePath
);
$temporaryFile->delete();
return $exported;
}
/**
* {@inheritdoc}
*/
public function queue($export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = [])
{
$writerType = FileTypeDetector::detectStrict($filePath, $writerType);
return $this->queuedWriter->store(
$export,
$filePath,
$disk,
$writerType,
$diskOptions
);
}
/**
* {@inheritdoc}
*/
public function raw($export, string $writerType)
{
$temporaryFile = $this->writer->export($export, $writerType);
$contents = $temporaryFile->contents();
$temporaryFile->delete();
return $contents;
}
/**
* {@inheritdoc}
*/
public function import($import, $filePath, string $disk = null, string $readerType = null)
{
$readerType = FileTypeDetector::detect($filePath, $readerType);
$response = $this->reader->read($import, $filePath, $readerType, $disk);
if ($response instanceof PendingDispatch) {
return $response;
}
return $this;
}
/**
* {@inheritdoc}
*/
public function toArray($import, $filePath, string $disk = null, string $readerType = null): array
{
$readerType = FileTypeDetector::detect($filePath, $readerType);
return $this->reader->toArray($import, $filePath, $readerType, $disk);
}
/**
* {@inheritdoc}
*/
public function toCollection($import, $filePath, string $disk = null, string $readerType = null): Collection
{
$readerType = FileTypeDetector::detect($filePath, $readerType);
return $this->reader->toCollection($import, $filePath, $readerType, $disk);
}
/**
* {@inheritdoc}
*/
public function queueImport(ShouldQueue $import, $filePath, string $disk = null, string $readerType = null)
{
return $this->import($import, $filePath, $disk, $readerType);
}
/**
* @param object $export
* @param string|null $fileName
* @param string $writerType
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @return TemporaryFile
*/
protected function export($export, string $fileName, string $writerType = null): TemporaryFile
{
$writerType = FileTypeDetector::detectStrict($fileName, $writerType);
return $this->writer->export($export, $writerType);
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Maatwebsite\Excel;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
use Laravel\Lumen\Application as LumenApplication;
use Maatwebsite\Excel\Console\ExportMakeCommand;
use Maatwebsite\Excel\Console\ImportMakeCommand;
use Maatwebsite\Excel\Files\Filesystem;
use Maatwebsite\Excel\Files\TemporaryFileFactory;
use Maatwebsite\Excel\Mixins\DownloadCollection;
use Maatwebsite\Excel\Mixins\StoreCollection;
use Maatwebsite\Excel\Transactions\TransactionHandler;
use Maatwebsite\Excel\Transactions\TransactionManager;
class ExcelServiceProvider extends ServiceProvider
{
/**
* {@inheritdoc}
*/
public function boot()
{
if ($this->app->runningInConsole()) {
if ($this->app instanceof LumenApplication) {
$this->app->configure('excel');
} else {
$this->publishes([
$this->getConfigFile() => config_path('excel.php'),
], 'config');
}
}
}
/**
* {@inheritdoc}
*/
public function register()
{
$this->mergeConfigFrom(
$this->getConfigFile(),
'excel'
);
$this->app->bind(TransactionManager::class, function () {
return new TransactionManager($this->app);
});
$this->app->bind(TransactionHandler::class, function () {
return $this->app->make(TransactionManager::class)->driver();
});
$this->app->bind(TemporaryFileFactory::class, function () {
return new TemporaryFileFactory(
config('excel.temporary_files.local_path', config('excel.exports.temp_path', storage_path('framework/laravel-excel'))),
config('excel.temporary_files.remote_disk')
);
});
$this->app->bind(Filesystem::class, function () {
return new Filesystem($this->app->make('filesystem'));
});
$this->app->bind('excel', function () {
return new Excel(
$this->app->make(Writer::class),
$this->app->make(QueuedWriter::class),
$this->app->make(Reader::class),
$this->app->make(Filesystem::class)
);
});
$this->app->alias('excel', Excel::class);
$this->app->alias('excel', Exporter::class);
$this->app->alias('excel', Importer::class);
Collection::mixin(new DownloadCollection);
Collection::mixin(new StoreCollection);
$this->commands([
ExportMakeCommand::class,
ImportMakeCommand::class,
]);
}
/**
* @return string
*/
protected function getConfigFile(): string
{
return __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'excel.php';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use LogicException;
class ConcernConflictException extends LogicException implements LaravelExcelException
{
/**
* @return ConcernConflictException
*/
public static function queryOrCollectionAndView()
{
return new static('Cannot use FromQuery, FromArray or FromCollection and FromView on the same sheet.');
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use Throwable;
interface LaravelExcelException extends Throwable
{
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use InvalidArgumentException;
use Throwable;
class NoFilePathGivenException extends InvalidArgumentException implements LaravelExcelException
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(
$message = 'A filepath needs to be passed.',
$code = 0,
Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
/**
* @return NoFilePathGivenException
*/
public static function import()
{
return new static('A filepath or UploadedFile needs to be passed to start the import.');
}
/**
* @return NoFilePathGivenException
*/
public static function export()
{
return new static('A filepath needs to be passed in order to store the export.');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use InvalidArgumentException;
use Throwable;
class NoFilenameGivenException extends InvalidArgumentException implements LaravelExcelException
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(
$message = 'A filename needs to be passed in order to download the export',
$code = 0,
Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use Exception;
use Throwable;
class NoTypeDetectedException extends Exception implements LaravelExcelException
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(
$message = 'No ReaderType or WriterType could be detected. Make sure you either pass a valid extension to the filename or pass an explicit type.',
$code = 0,
Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use Exception;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Validators\Failure;
class RowSkippedException extends Exception
{
/**
* @var Failure[]
*/
private $failures;
/**
* @param Failure ...$failures
*/
public function __construct(Failure ...$failures)
{
$this->failures = $failures;
parent::__construct();
}
/**
* @return Failure[]|Collection
*/
public function failures(): Collection
{
return new Collection($this->failures);
}
/**
* @return int[]
*/
public function skippedRows(): array
{
return $this->failures()->map->row()->all();
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
class SheetNotFoundException extends \Exception implements LaravelExcelException
{
/**
* @param string $name
*
* @return SheetNotFoundException
*/
public static function byName(string $name): SheetNotFoundException
{
return new static("Your requested sheet name [{$name}] is out of bounds.");
}
/**
* @param int $index
* @param int $sheetCount
*
* @return SheetNotFoundException
*/
public static function byIndex(int $index, int $sheetCount): SheetNotFoundException
{
return new static("Your requested sheet index: {$index} is out of bounds. The actual number of sheets is {$sheetCount}.");
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Maatwebsite\Excel\Exceptions;
use Exception;
use Throwable;
class UnreadableFileException extends Exception implements LaravelExcelException
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(
$message = 'File could not be read',
$code = 0,
Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Maatwebsite\Excel;
use Illuminate\Foundation\Bus\PendingDispatch;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
interface Exporter
{
/**
* @param object $export
* @param string|null $fileName
* @param string $writerType
* @param array $headers
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @return BinaryFileResponse
*/
public function download($export, string $fileName, string $writerType = null, array $headers = []);
/**
* @param object $export
* @param string $filePath
* @param string|null $disk
* @param string $writerType
* @param mixed $diskOptions
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @return bool
*/
public function store($export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = []);
/**
* @param object $export
* @param string $filePath
* @param string|null $disk
* @param string $writerType
* @param mixed $diskOptions
*
* @return PendingDispatch
*/
public function queue($export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = []);
/**
* @param object $export
* @param string $writerType
*
* @return string
*/
public function raw($export, string $writerType);
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Maatwebsite\Excel\Facades;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Maatwebsite\Excel\Excel as BaseExcel;
use Maatwebsite\Excel\Fakes\ExcelFake;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* @method static BinaryFileResponse download(object $export, string $fileName, string $writerType = null, array $headers = [])
* @method static bool store(object $export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = [])
* @method static PendingDispatch queue(object $export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = [])
* @method static BaseExcel import(object $import, string $filePath, string $disk = null, string $readerType = null)
* @method static array toArray(object $import, string $filePath, string $disk = null, string $readerType = null)
* @method static Collection toCollection(object $import, string $filePath, string $disk = null, string $readerType = null)
* @method static PendingDispatch queueImport(object $import, string $filePath, string $disk = null, string $readerType = null)
* @method static void matchByRegex()
* @method static void doNotMatchByRegex()
* @method static void assertDownloaded(string $fileName, callable $callback = null)
* @method static void assertStored(string $filePath, string $disk = null, callable $callback = null)
* @method static void assertQueued(string $filePath, string $disk = null, callable $callback = null)
* @method static void assertImported(string $filePath, string $disk = null, callable $callback = null)
*/
class Excel extends Facade
{
/**
* Replace the bound instance with a fake.
*
* @return void
*/
public static function fake()
{
static::swap(new ExcelFake());
}
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'excel';
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Maatwebsite\Excel\Factories;
use Maatwebsite\Excel\Concerns\MapsCsvSettings;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Maatwebsite\Excel\Exceptions\NoTypeDetectedException;
use Maatwebsite\Excel\Files\TemporaryFile;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
use PhpOffice\PhpSpreadsheet\Reader\Exception;
use PhpOffice\PhpSpreadsheet\Reader\IReader;
class ReaderFactory
{
use MapsCsvSettings;
/**
* @param object $import
* @param TemporaryFile $file
* @param string $readerType
*
* @throws Exception
* @return IReader
*/
public static function make($import, TemporaryFile $file, string $readerType = null): IReader
{
$reader = IOFactory::createReader(
$readerType ?: static::identify($file)
);
if (method_exists($reader, 'setReadDataOnly')) {
$reader->setReadDataOnly(config('excel.imports.read_only', true));
}
if ($reader instanceof Csv) {
static::applyCsvSettings(config('excel.imports.csv', []));
if ($import instanceof WithCustomCsvSettings) {
static::applyCsvSettings($import->getCsvSettings());
}
$reader->setDelimiter(static::$delimiter);
$reader->setEnclosure(static::$enclosure);
$reader->setEscapeCharacter(static::$escapeCharacter);
$reader->setContiguous(static::$contiguous);
$reader->setInputEncoding(static::$inputEncoding);
}
return $reader;
}
/**
* @param TemporaryFile $temporaryFile
*
* @throws NoTypeDetectedException
* @return string
*/
private static function identify(TemporaryFile $temporaryFile): string
{
try {
return IOFactory::identify($temporaryFile->getLocalPath());
} catch (Exception $e) {
throw new NoTypeDetectedException(null, null, $e);
}
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Maatwebsite\Excel\Factories;
use Maatwebsite\Excel\Concerns\MapsCsvSettings;
use Maatwebsite\Excel\Concerns\WithCharts;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithPreCalculateFormulas;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
use PhpOffice\PhpSpreadsheet\Writer\Html;
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
class WriterFactory
{
use MapsCsvSettings;
/**
* @param string $writerType
* @param Spreadsheet $spreadsheet
* @param object $export
*
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @return IWriter
*/
public static function make(string $writerType, Spreadsheet $spreadsheet, $export): IWriter
{
$writer = IOFactory::createWriter($spreadsheet, $writerType);
if (static::includesCharts($export)) {
$writer->setIncludeCharts(true);
}
if ($writer instanceof Html && $export instanceof WithMultipleSheets) {
$writer->writeAllSheets();
}
if ($writer instanceof Csv) {
static::applyCsvSettings(config('excel.exports.csv', []));
if ($export instanceof WithCustomCsvSettings) {
static::applyCsvSettings($export->getCsvSettings());
}
$writer->setDelimiter(static::$delimiter);
$writer->setEnclosure(static::$enclosure);
$writer->setLineEnding(static::$lineEnding);
$writer->setUseBOM(static::$useBom);
$writer->setIncludeSeparatorLine(static::$includeSeparatorLine);
$writer->setExcelCompatibility(static::$excelCompatibility);
}
// Calculation settings
$writer->setPreCalculateFormulas(
$export instanceof WithPreCalculateFormulas
? true
: config('excel.exports.pre_calculate_formulas', false)
);
return $writer;
}
/**
* @param $export
*
* @return bool
*/
private static function includesCharts($export): bool
{
if ($export instanceof WithCharts) {
return true;
}
if ($export instanceof WithMultipleSheets) {
foreach ($export->sheets() as $sheet) {
if ($sheet instanceof WithCharts) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,342 @@
<?php
namespace Maatwebsite\Excel\Fakes;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Queue;
use Maatwebsite\Excel\Exporter;
use Maatwebsite\Excel\Importer;
use Maatwebsite\Excel\Reader;
use PHPUnit\Framework\Assert;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class ExcelFake implements Exporter, Importer
{
/**
* @var array
*/
protected $downloads = [];
/**
* @var array
*/
protected $stored = [];
/**
* @var array
*/
protected $queued = [];
/**
* @var array
*/
protected $imported = [];
/**
* @var bool
*/
protected $matchByRegex = false;
/**
* {@inheritdoc}
*/
public function download($export, string $fileName, string $writerType = null, array $headers = [])
{
$this->downloads[$fileName] = $export;
return new BinaryFileResponse(__DIR__ . '/fake_file');
}
/**
* {@inheritdoc}
*/
public function store($export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = [])
{
if ($export instanceof ShouldQueue) {
return $this->queue($export, $filePath, $disk, $writerType);
}
$this->stored[$disk ?? 'default'][$filePath] = $export;
return true;
}
/**
* {@inheritdoc}
*/
public function queue($export, string $filePath, string $disk = null, string $writerType = null, $diskOptions = [])
{
Queue::fake();
$this->stored[$disk ?? 'default'][$filePath] = $export;
$this->queued[$disk ?? 'default'][$filePath] = $export;
return new PendingDispatch(new class {
use Queueable;
public function handle()
{
//
}
});
}
/**
* @param object $export
* @param string $writerType
*
* @return string
*/
public function raw($export, string $writerType)
{
return 'RAW-CONTENTS';
}
/**
* @param object $import
* @param string|UploadedFile $file
* @param string|null $disk
* @param string|null $readerType
*
* @return Reader|PendingDispatch
*/
public function import($import, $file, string $disk = null, string $readerType = null)
{
if ($import instanceof ShouldQueue) {
return $this->queueImport($import, $file, $disk, $readerType);
}
$filePath = ($file instanceof UploadedFile) ? $file->getClientOriginalName() : $file;
$this->imported[$disk ?? 'default'][$filePath] = $import;
return $this;
}
/**
* @param object $import
* @param string|UploadedFile $file
* @param string|null $disk
* @param string|null $readerType
*
* @return array
*/
public function toArray($import, $file, string $disk = null, string $readerType = null): array
{
$filePath = ($file instanceof UploadedFile) ? $file->getFilename() : $file;
$this->imported[$disk ?? 'default'][$filePath] = $import;
return [];
}
/**
* @param object $import
* @param string|UploadedFile $file
* @param string|null $disk
* @param string|null $readerType
*
* @return Collection
*/
public function toCollection($import, $file, string $disk = null, string $readerType = null): Collection
{
$filePath = ($file instanceof UploadedFile) ? $file->getFilename() : $file;
$this->imported[$disk ?? 'default'][$filePath] = $import;
return new Collection();
}
/**
* @param ShouldQueue $import
* @param string|UploadedFile $file
* @param string|null $disk
* @param string $readerType
*
* @return PendingDispatch
*/
public function queueImport(ShouldQueue $import, $file, string $disk = null, string $readerType = null)
{
Queue::fake();
$filePath = ($file instanceof UploadedFile) ? $file->getFilename() : $file;
$this->queued[$disk ?? 'default'][$filePath] = $import;
$this->imported[$disk ?? 'default'][$filePath] = $import;
return new PendingDispatch(new class {
use Queueable;
public function handle()
{
//
}
});
}
/**
* When asserting downloaded, stored, queued or imported, use regular expression
* to look for a matching file path.
*
* @return void
*/
public function matchByRegex()
{
$this->matchByRegex = true;
}
/**
* When asserting downloaded, stored, queued or imported, use regular string
* comparison for matching file path.
*
* @return void
*/
public function doNotMatchByRegex()
{
$this->matchByRegex = false;
}
/**
* @param string $fileName
* @param callable|null $callback
*/
public function assertDownloaded(string $fileName, $callback = null)
{
$fileName = $this->assertArrayHasKey($fileName, $this->downloads, sprintf('%s is not downloaded', $fileName));
$callback = $callback ?: function () {
return true;
};
Assert::assertTrue(
$callback($this->downloads[$fileName]),
"The file [{$fileName}] was not downloaded with the expected data."
);
}
/**
* @param string $filePath
* @param string|callable|null $disk
* @param callable|null $callback
*/
public function assertStored(string $filePath, $disk = null, $callback = null)
{
if (is_callable($disk)) {
$callback = $disk;
$disk = null;
}
$disk = $disk ?? 'default';
$storedOnDisk = $this->stored[$disk] ?? [];
$filePath = $this->assertArrayHasKey(
$filePath,
$storedOnDisk,
sprintf('%s is not stored on disk %s', $filePath, $disk)
);
$callback = $callback ?: function () {
return true;
};
Assert::assertTrue(
$callback($storedOnDisk[$filePath]),
"The file [{$filePath}] was not stored with the expected data."
);
}
/**
* @param string $filePath
* @param string|callable|null $disk
* @param callable|null $callback
*/
public function assertQueued(string $filePath, $disk = null, $callback = null)
{
if (is_callable($disk)) {
$callback = $disk;
$disk = null;
}
$disk = $disk ?? 'default';
$queuedForDisk = $this->queued[$disk] ?? [];
$filePath = $this->assertArrayHasKey(
$filePath,
$queuedForDisk,
sprintf('%s is not queued for export on disk %s', $filePath, $disk)
);
$callback = $callback ?: function () {
return true;
};
Assert::assertTrue(
$callback($queuedForDisk[$filePath]),
"The file [{$filePath}] was not stored with the expected data."
);
}
/**
* @param string $filePath
* @param string|callable|null $disk
* @param callable|null $callback
*/
public function assertImported(string $filePath, $disk = null, $callback = null)
{
if (is_callable($disk)) {
$callback = $disk;
$disk = null;
}
$disk = $disk ?? 'default';
$importedOnDisk = $this->imported[$disk] ?? [];
$filePath = $this->assertArrayHasKey(
$filePath,
$importedOnDisk,
sprintf('%s is not stored on disk %s', $filePath, $disk)
);
$callback = $callback ?: function () {
return true;
};
Assert::assertTrue(
$callback($importedOnDisk[$filePath]),
"The file [{$filePath}] was not imported with the expected data."
);
}
/**
* Asserts that an array has a specified key and returns the key if successful.
* @see matchByRegex for more information about file path matching
*
* @param string $key
* @param array $array
* @param string $message
*
* @return string
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
protected function assertArrayHasKey(string $key, array $disk, string $message = ''): string
{
if ($this->matchByRegex) {
$files = array_keys($disk);
$results = preg_grep($key, $files);
Assert::assertGreaterThan(0, count($results), $message);
Assert::assertEquals(1, count($results), "More than one result matches the file name expression '$key'.");
return $results[0];
}
Assert::assertArrayHasKey($key, $disk, $message);
return $key;
}
}

View File

View File

@@ -0,0 +1,99 @@
<?php
namespace Maatwebsite\Excel\Files;
use Illuminate\Contracts\Filesystem\Filesystem as IlluminateFilesystem;
/**
* @method bool get(string $filename)
* @method resource readStream(string $filename)
* @method bool delete(string $filename)
* @method bool exists(string $filename)
*/
class Disk
{
/**
* @var IlluminateFilesystem
*/
protected $disk;
/**
* @var string|null
*/
protected $name;
/**
* @var array
*/
protected $diskOptions;
/**
* @param IlluminateFilesystem $disk
* @param string|null $name
* @param array $diskOptions
*/
public function __construct(IlluminateFilesystem $disk, string $name = null, array $diskOptions = [])
{
$this->disk = $disk;
$this->name = $name;
$this->diskOptions = $diskOptions;
}
/**
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public function __call($name, $arguments)
{
return $this->disk->{$name}(...$arguments);
}
/**
* @param string $destination
* @param string|resource $contents
*
* @return bool
*/
public function put(string $destination, $contents): bool
{
return $this->disk->put($destination, $contents, $this->diskOptions);
}
/**
* @param TemporaryFile $source
* @param string $destination
*
* @return bool
*/
public function copy(TemporaryFile $source, string $destination): bool
{
$readStream = $source->readStream();
if (realpath($destination)) {
$tempStream = fopen($destination, 'rb+');
$success = stream_copy_to_stream($readStream, $tempStream) !== false;
if (is_resource($tempStream)) {
fclose($tempStream);
}
} else {
$success = $this->put($destination, $readStream);
}
if (is_resource($readStream)) {
fclose($readStream);
}
return $success;
}
/**
* @param string $filename
*/
public function touch(string $filename)
{
$this->disk->put($filename, '', $this->diskOptions);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Maatwebsite\Excel\Files;
use Illuminate\Contracts\Filesystem\Factory;
class Filesystem
{
/**
* @var Factory
*/
private $filesystem;
/**
* @param Factory $filesystem
*/
public function __construct(Factory $filesystem)
{
$this->filesystem = $filesystem;
}
/**
* @param string|null $disk
* @param array $diskOptions
*
* @return Disk
*/
public function disk(string $disk = null, array $diskOptions = []): Disk
{
return new Disk(
$this->filesystem->disk($disk),
$disk,
$diskOptions
);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Maatwebsite\Excel\Files;
class LocalTemporaryFile extends TemporaryFile
{
/**
* @var string
*/
private $filePath;
/**
* @param string $filePath
*/
public function __construct(string $filePath)
{
touch($filePath);
$this->filePath = realpath($filePath);
}
/**
* @return string
*/
public function getLocalPath(): string
{
return $this->filePath;
}
/**
* @return bool
*/
public function exists(): bool
{
return file_exists($this->filePath);
}
/**
* @return bool
*/
public function delete(): bool
{
if (@unlink($this->filePath) || !$this->exists()) {
return true;
}
return unlink($this->filePath);
}
/**
* @return resource
*/
public function readStream()
{
return fopen($this->getLocalPath(), 'rb+');
}
/**
* @return string
*/
public function contents(): string
{
return file_get_contents($this->filePath);
}
/**
* @param @param string|resource $contents
*/
public function put($contents)
{
file_put_contents($this->filePath, $contents);
}
}

View File

@@ -0,0 +1,131 @@
<?php
namespace Maatwebsite\Excel\Files;
class RemoteTemporaryFile extends TemporaryFile
{
/**
* @var string
*/
private $disk;
/**
* @var Disk|null
*/
private $diskInstance;
/**
* @var string
*/
private $filename;
/**
* @var LocalTemporaryFile
*/
private $localTemporaryFile;
/**
* @param string $disk
* @param string $filename
* @param LocalTemporaryFile $localTemporaryFile
*/
public function __construct(string $disk, string $filename, LocalTemporaryFile $localTemporaryFile)
{
$this->disk = $disk;
$this->filename = $filename;
$this->localTemporaryFile = $localTemporaryFile;
$this->disk()->touch($filename);
}
public function __sleep()
{
return ['disk', 'filename', 'localTemporaryFile'];
}
/**
* @return string
*/
public function getLocalPath(): string
{
return $this->localTemporaryFile->getLocalPath();
}
/**
* @return bool
*/
public function exists(): bool
{
return $this->disk()->exists($this->filename);
}
/**
* @return bool
*/
public function delete(): bool
{
$this->localTemporaryFile->delete();
return $this->disk()->delete($this->filename);
}
/**
* @return TemporaryFile
*/
public function sync(): TemporaryFile
{
if (!$this->localTemporaryFile->exists()) {
touch($this->localTemporaryFile->getLocalPath());
}
$this->disk()->copy(
$this,
$this->localTemporaryFile->getLocalPath()
);
return $this;
}
/**
* Store on remote disk.
*/
public function updateRemote()
{
$this->disk()->copy(
$this->localTemporaryFile,
$this->filename
);
}
/**
* @return resource
*/
public function readStream()
{
return $this->disk()->readStream($this->filename);
}
/**
* @return string
*/
public function contents(): string
{
return $this->disk()->get($this->filename);
}
/**
* @param string|resource $contents
*/
public function put($contents)
{
$this->disk()->put($this->filename, $contents);
}
/**
* @return Disk
*/
public function disk(): Disk
{
return $this->diskInstance ?: $this->diskInstance = app(Filesystem::class)->disk($this->disk);
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Maatwebsite\Excel\Files;
use Symfony\Component\HttpFoundation\File\UploadedFile;
abstract class TemporaryFile
{
/**
* @return string
*/
abstract public function getLocalPath(): string;
/**
* @return bool
*/
abstract public function exists(): bool;
/**
* @param @param string|resource $contents
*/
abstract public function put($contents);
/**
* @return bool
*/
abstract public function delete(): bool;
/**
* @return resource
*/
abstract public function readStream();
/**
* @return string
*/
abstract public function contents(): string;
/**
* @return TemporaryFile
*/
public function sync(): TemporaryFile
{
return $this;
}
/**
* @param string|UploadedFile $filePath
* @param string|null $disk
*
* @return TemporaryFile
*/
public function copyFrom($filePath, string $disk = null): TemporaryFile
{
if ($filePath instanceof UploadedFile) {
$readStream = fopen($filePath->getRealPath(), 'rb');
} elseif ($disk === null && realpath($filePath) !== false) {
$readStream = fopen($filePath, 'rb');
} else {
$readStream = app('filesystem')->disk($disk)->readStream($filePath);
}
$this->put($readStream);
if (is_resource($readStream)) {
fclose($readStream);
}
return $this->sync();
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Maatwebsite\Excel\Files;
use Illuminate\Support\Str;
class TemporaryFileFactory
{
/**
* @var string|null
*/
private $temporaryPath;
/**
* @var string|null
*/
private $temporaryDisk;
/**
* @param string|null $temporaryPath
* @param string|null $temporaryDisk
*/
public function __construct(string $temporaryPath = null, string $temporaryDisk = null)
{
$this->temporaryPath = $temporaryPath;
$this->temporaryDisk = $temporaryDisk;
}
/**
* @param string|null $fileExtension
*
* @return TemporaryFile
*/
public function make(string $fileExtension = null): TemporaryFile
{
if (null !== $this->temporaryDisk) {
return $this->makeRemote();
}
return $this->makeLocal(null, $fileExtension);
}
/**
* @param string|null $fileName
*
* @param string|null $fileExtension
*
* @return LocalTemporaryFile
*/
public function makeLocal(string $fileName = null, string $fileExtension = null): LocalTemporaryFile
{
if (!file_exists($this->temporaryPath) && !mkdir($concurrentDirectory = $this->temporaryPath) && !is_dir($concurrentDirectory)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
}
return new LocalTemporaryFile(
$this->temporaryPath . DIRECTORY_SEPARATOR . ($fileName ?: $this->generateFilename($fileExtension))
);
}
/**
* @return RemoteTemporaryFile
*/
private function makeRemote(): RemoteTemporaryFile
{
$filename = $this->generateFilename();
return new RemoteTemporaryFile(
$this->temporaryDisk,
config('excel.temporary_files.remote_prefix') . $filename,
$this->makeLocal($filename)
);
}
/**
* @param string|null $fileExtension
*
* @return string
*/
private function generateFilename(string $fileExtension = null): string
{
return 'laravel-excel-' . Str::random(32) . ($fileExtension ? '.' . $fileExtension : '');
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Maatwebsite\Excel\Filters;
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
class ChunkReadFilter implements IReadFilter
{
/**
* @var int
*/
private $headingRow;
/**
* @var int
*/
private $startRow;
/**
* @var int
*/
private $endRow;
/**
* @var string
*/
private $worksheetName;
/**
* @param int $headingRow
* @param int $startRow
* @param int $chunkSize
* @param string $worksheetName
*/
public function __construct(int $headingRow, int $startRow, int $chunkSize, string $worksheetName)
{
$this->headingRow = $headingRow;
$this->startRow = $startRow;
$this->endRow = $startRow + $chunkSize;
$this->worksheetName = $worksheetName;
}
/**
* @param string $column
* @param int $row
* @param string $worksheetName
*
* @return bool
*/
public function readCell($column, $row, $worksheetName = '')
{
// Only read the heading row, and the rows that are configured in $this->_startRow and $this->_endRow
return ($worksheetName === $this->worksheetName || $worksheetName === '')
&& ($row === $this->headingRow || ($row >= $this->startRow && $row < $this->endRow));
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Maatwebsite\Excel;
trait HasEventBus
{
/**
* @var array
*/
protected static $globalEvents = [];
/**
* @var array
*/
protected $events = [];
/**
* Register local event listeners.
*
* @param array $listeners
*/
public function registerListeners(array $listeners)
{
foreach ($listeners as $event => $listener) {
$this->events[$event][] = $listener;
}
}
/**
* Register a global event listener.
*
* @param string $event
* @param callable $listener
*/
public static function listen(string $event, callable $listener)
{
static::$globalEvents[$event][] = $listener;
}
/**
* @param object $event
*/
public function raise($event)
{
foreach ($this->listeners($event) as $listener) {
$listener($event);
}
}
/**
* @param object $event
*
* @return callable[]
*/
public function listeners($event): array
{
$name = \get_class($event);
$localListeners = $this->events[$name] ?? [];
$globalListeners = static::$globalEvents[$name] ?? [];
return array_merge($globalListeners, $localListeners);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Maatwebsite\Excel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithLimit;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
class HeadingRowImport implements WithStartRow, WithLimit, WithMapping
{
use Importable;
/**
* @var int
*/
private $headingRow;
/**
* @param int $headingRow
*/
public function __construct(int $headingRow = 1)
{
$this->headingRow = $headingRow;
}
/**
* @return int
*/
public function startRow(): int
{
return $this->headingRow;
}
/**
* @return int
*/
public function limit(): int
{
return 1;
}
/**
* @param mixed $row
*
* @return array
*/
public function map($row): array
{
return HeadingRowFormatter::format($row);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Maatwebsite\Excel\Helpers;
class ArrayHelper
{
/**
* @param array $array
*
* @return array
*/
public static function ensureMultipleRows(array $array): array
{
if (static::hasMultipleRows($array)) {
return $array;
}
return [$array];
}
/**
* Only have multiple rows, if each
* element in the array is an array itself.
*
* @param array $array
*
* @return bool
*/
public static function hasMultipleRows(array $array): bool
{
return count($array) === count(array_filter($array, 'is_array'));
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Maatwebsite\Excel\Helpers;
class CellHelper
{
/**
* @param string $coordinate
*
* @return string
*/
public static function getColumnFromCoordinate(string $coordinate): string
{
return preg_replace('/[0-9]/', '', $coordinate);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Maatwebsite\Excel\Helpers;
use Maatwebsite\Excel\Exceptions\NoTypeDetectedException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileTypeDetector
{
/**
* @param $filePath
* @param string|null $type
*
* @throws NoTypeDetectedException
* @return string|null
*/
public static function detect($filePath, string $type = null)
{
if (null !== $type) {
return $type;
}
if (!$filePath instanceof UploadedFile) {
$pathInfo = pathinfo($filePath);
$extension = $pathInfo['extension'] ?? '';
} else {
$extension = $filePath->getClientOriginalExtension();
}
if (null === $type && trim($extension) === '') {
throw new NoTypeDetectedException();
}
return config('excel.extension_detector.' . strtolower($extension));
}
/**
* @param string $filePath
* @param string|null $type
*
* @throws NoTypeDetectedException
* @return string
*/
public static function detectStrict(string $filePath, string $type = null): string
{
$type = static::detect($filePath, $type);
if (!$type) {
throw new NoTypeDetectedException();
}
return $type;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Maatwebsite\Excel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Support\Collection;
use Symfony\Component\HttpFoundation\File\UploadedFile;
interface Importer
{
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @return Reader|PendingDispatch
*/
public function import($import, $filePath, string $disk = null, string $readerType = null);
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @return array
*/
public function toArray($import, $filePath, string $disk = null, string $readerType = null): array;
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string|null $disk
* @param string|null $readerType
*
* @return Collection
*/
public function toCollection($import, $filePath, string $disk = null, string $readerType = null): Collection;
/**
* @param ShouldQueue $import
* @param string|UploadedFile $filePath
* @param string|null $disk
* @param string $readerType
*
* @return PendingDispatch
*/
public function queueImport(ShouldQueue $import, $filePath, string $disk = null, string $readerType = null);
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Maatwebsite\Excel\Imports;
use Maatwebsite\Excel\Concerns\WithLimit;
class EndRowFinder
{
/**
* @param object|WithLimit $import
* @param int $startRow
*
* @return int|null
*/
public static function find($import, int $startRow = null)
{
if (!$import instanceof WithLimit) {
return null;
}
// When no start row given,
// use the first row as start row.
$startRow = $startRow ?? 1;
// Subtract 1 row from the start row, so a limit
// of 1 row, will have the same start and end row.
return ($startRow - 1) + $import->limit();
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Maatwebsite\Excel\Imports;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Row;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class HeadingRowExtractor
{
/**
* @const int
*/
const DEFAULT_HEADING_ROW = 1;
/**
* @param WithHeadingRow|mixed $importable
*
* @return int
*/
public static function headingRow($importable): int
{
return method_exists($importable, 'headingRow')
? $importable->headingRow()
: self::DEFAULT_HEADING_ROW;
}
/**
* @param WithHeadingRow|mixed $importable
*
* @return int
*/
public static function determineStartRow($importable): int
{
if ($importable instanceof WithStartRow) {
return $importable->startRow();
}
// The start row is the row after the heading row if we have one!
return $importable instanceof WithHeadingRow
? self::headingRow($importable) + 1
: self::DEFAULT_HEADING_ROW;
}
/**
* @param Worksheet $worksheet
* @param WithHeadingRow|mixed $importable
*
* @return array
*/
public static function extract(Worksheet $worksheet, $importable): array
{
if (!$importable instanceof WithHeadingRow) {
return [];
}
$headingRowNumber = self::headingRow($importable);
$rows = iterator_to_array($worksheet->getRowIterator($headingRowNumber, $headingRowNumber));
$headingRow = head($rows);
return HeadingRowFormatter::format((new Row($headingRow))->toArray(null, false, false));
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace Maatwebsite\Excel\Imports;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use InvalidArgumentException;
class HeadingRowFormatter
{
/**
* @const string
*/
const FORMATTER_NONE = 'none';
/**
* @const string
*/
const FORMATTER_SLUG = 'slug';
/**
* @var string
*/
protected static $formatter;
/**
* @var callable[]
*/
protected static $customFormatters = [];
/**
* @var array
*/
protected static $defaultFormatters = [
self::FORMATTER_NONE,
self::FORMATTER_SLUG,
];
/**
* @param array $headings
*
* @return array
*/
public static function format(array $headings): array
{
return (new Collection($headings))->map(function ($value) {
return static::callFormatter($value);
})->toArray();
}
/**
* @param string $name
*/
public static function default(string $name = null)
{
if (null !== $name && !isset(static::$customFormatters[$name]) && !in_array($name, static::$defaultFormatters, true)) {
throw new InvalidArgumentException(sprintf('Formatter "%s" does not exist', $name));
}
static::$formatter = $name;
}
/**
* @param string $name
* @param callable $formatter
*/
public static function extend(string $name, callable $formatter)
{
static::$customFormatters[$name] = $formatter;
}
/**
* Reset the formatter.
*/
public static function reset()
{
static::default();
}
/**
* @param mixed $value
*
* @return mixed
*/
protected static function callFormatter($value)
{
static::$formatter = static::$formatter ?? config('excel.imports.heading_row.formatter', self::FORMATTER_SLUG);
// Call custom formatter
if (isset(static::$customFormatters[static::$formatter])) {
$formatter = static::$customFormatters[static::$formatter];
return $formatter($value);
}
if (static::$formatter === self::FORMATTER_SLUG) {
return Str::slug($value, '_');
}
// No formatter (FORMATTER_NONE)
return $value;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Maatwebsite\Excel\Imports;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithCalculatedFormulas;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithProgressBar;
use Maatwebsite\Excel\Row;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class ModelImporter
{
/**
* @var ModelManager
*/
private $manager;
/**
* @param ModelManager $manager
*/
public function __construct(ModelManager $manager)
{
$this->manager = $manager;
}
/**
* @param Worksheet $worksheet
* @param ToModel $import
* @param int|null $startRow
*/
public function import(Worksheet $worksheet, ToModel $import, int $startRow = 1)
{
$headingRow = HeadingRowExtractor::extract($worksheet, $import);
$batchSize = $import instanceof WithBatchInserts ? $import->batchSize() : 1;
$endRow = EndRowFinder::find($import, $startRow);
$progessBar = $import instanceof WithProgressBar;
$withMapping = $import instanceof WithMapping;
$withCalcFormulas = $import instanceof WithCalculatedFormulas;
$i = 0;
foreach ($worksheet->getRowIterator($startRow, $endRow) as $spreadSheetRow) {
$i++;
$row = new Row($spreadSheetRow, $headingRow);
$rowArray = $row->toArray(null, $withCalcFormulas);
if ($withMapping) {
$rowArray = $import->map($rowArray);
}
$this->manager->add(
$row->getIndex(),
$rowArray
);
// Flush each batch.
if (($i % $batchSize) === 0) {
$this->manager->flush($import, $batchSize > 1);
$i = 0;
if ($progessBar) {
$import->getConsoleOutput()->progressAdvance($batchSize);
}
}
}
// Flush left-overs.
$this->manager->flush($import, $batchSize > 1);
}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace Maatwebsite\Excel\Imports;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\SkipsOnError;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Exceptions\RowSkippedException;
use Maatwebsite\Excel\Validators\RowValidator;
use Maatwebsite\Excel\Validators\ValidationException;
use Throwable;
class ModelManager
{
/**
* @var array
*/
private $rows = [];
/**
* @var RowValidator
*/
private $validator;
/**
* @param RowValidator $validator
*/
public function __construct(RowValidator $validator)
{
$this->validator = $validator;
}
/**
* @param int $row
* @param array $attributes
*/
public function add(int $row, array $attributes)
{
$this->rows[$row] = $attributes;
}
/**
* @param ToModel $import
* @param bool $massInsert
*
* @throws ValidationException
*/
public function flush(ToModel $import, bool $massInsert = false)
{
if ($import instanceof WithValidation) {
$this->validateRows($import);
}
if ($massInsert) {
$this->massFlush($import);
} else {
$this->singleFlush($import);
}
$this->rows = [];
}
/**
* @param ToModel $import
* @param array $attributes
*
* @return Model[]|Collection
*/
public function toModels(ToModel $import, array $attributes): Collection
{
$model = $import->model($attributes);
if (null !== $model) {
return \is_array($model) ? new Collection($model) : new Collection([$model]);
}
return new Collection([]);
}
/**
* @param ToModel $import
*/
private function massFlush(ToModel $import)
{
$this->rows()
->flatMap(function (array $attributes) use ($import) {
return $this->toModels($import, $attributes);
})
->mapToGroups(function ($model) {
return [\get_class($model) => $this->prepare($model)->getAttributes()];
})
->each(function (Collection $models, string $model) use ($import) {
try {
/* @var Model $model */
$model::query()->insert($models->toArray());
} catch (Throwable $e) {
if ($import instanceof SkipsOnError) {
$import->onError($e);
} else {
throw $e;
}
}
});
}
/**
* @param ToModel $import
*/
private function singleFlush(ToModel $import)
{
$this
->rows()
->each(function (array $attributes) use ($import) {
$this->toModels($import, $attributes)->each(function (Model $model) use ($import) {
try {
$model->saveOrFail();
} catch (Throwable $e) {
if ($import instanceof SkipsOnError) {
$import->onError($e);
} else {
throw $e;
}
}
});
});
}
/**
* @param Model $model
*
* @return Model
*/
private function prepare(Model $model): Model
{
if ($model->usesTimestamps()) {
$time = $model->freshTimestamp();
$updatedAtColumn = $model->getUpdatedAtColumn();
// If model has updated at column and not manually provided.
if ($updatedAtColumn && null === $model->{$updatedAtColumn}) {
$model->setUpdatedAt($time);
}
$createdAtColumn = $model->getCreatedAtColumn();
// If model has created at column and not manually provided.
if ($createdAtColumn && null === $model->{$createdAtColumn}) {
$model->setCreatedAt($time);
}
}
return $model;
}
/**
* @param WithValidation $import
*
* @throws ValidationException
*/
private function validateRows(WithValidation $import)
{
try {
$this->validator->validate($this->rows, $import);
} catch (RowSkippedException $e) {
foreach ($e->skippedRows() as $row) {
unset($this->rows[$row]);
}
}
}
/**
* @return Collection
*/
private function rows(): Collection
{
return new Collection($this->rows);
}
}

Some files were not shown because too many files have changed in this diff Show More