Current File : /home/tradevaly/public_html/vendor/maatwebsite/excel/src/Reader.php |
<?php
namespace Maatwebsite\Excel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Collection;
use InvalidArgumentException;
use Maatwebsite\Excel\Concerns\HasReferencesToOtherSheets;
use Maatwebsite\Excel\Concerns\SkipsUnknownSheets;
use Maatwebsite\Excel\Concerns\WithCalculatedFormulas;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithFormatData;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Events\AfterImport;
use Maatwebsite\Excel\Events\BeforeImport;
use Maatwebsite\Excel\Events\ImportFailed;
use Maatwebsite\Excel\Exceptions\NoTypeDetectedException;
use Maatwebsite\Excel\Exceptions\SheetNotFoundException;
use Maatwebsite\Excel\Factories\ReaderFactory;
use Maatwebsite\Excel\Files\TemporaryFile;
use Maatwebsite\Excel\Files\TemporaryFileFactory;
use Maatwebsite\Excel\Transactions\TransactionHandler;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Reader\Exception;
use PhpOffice\PhpSpreadsheet\Reader\IReader;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Throwable;
/** @mixin Spreadsheet */
class Reader
{
use DelegatedMacroable, HasEventBus;
/**
* @var Spreadsheet
*/
protected $spreadsheet;
/**
* @var object[]
*/
protected $sheetImports = [];
/**
* @var TemporaryFile
*/
protected $currentFile;
/**
* @var TemporaryFileFactory
*/
protected $temporaryFileFactory;
/**
* @var TransactionHandler
*/
protected $transaction;
/**
* @var IReader
*/
protected $reader;
/**
* @param TemporaryFileFactory $temporaryFileFactory
* @param TransactionHandler $transaction
*/
public function __construct(TemporaryFileFactory $temporaryFileFactory, TransactionHandler $transaction)
{
$this->setDefaultValueBinder();
$this->transaction = $transaction;
$this->temporaryFileFactory = $temporaryFileFactory;
}
public function __sleep()
{
return ['spreadsheet', 'sheetImports', 'currentFile', 'temporaryFileFactory', 'reader'];
}
public function __wakeup()
{
$this->transaction = app(TransactionHandler::class);
}
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string|null $readerType
* @param string|null $disk
* @return \Illuminate\Foundation\Bus\PendingDispatch|$this
*
* @throws NoTypeDetectedException
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws Exception
*/
public function read($import, $filePath, string $readerType = null, string $disk = null)
{
$this->reader = $this->getReader($import, $filePath, $readerType, $disk);
if ($import instanceof WithChunkReading) {
return (new ChunkReader)->read($import, $this, $this->currentFile);
}
try {
$this->loadSpreadsheet($import, $this->reader);
($this->transaction)(function () use ($import) {
$sheetsToDisconnect = [];
foreach ($this->sheetImports as $index => $sheetImport) {
if ($sheet = $this->getSheet($import, $sheetImport, $index)) {
$sheet->import($sheetImport, $sheet->getStartRow($sheetImport));
// when using WithCalculatedFormulas we need to keep the sheet until all sheets are imported
if (!($sheetImport instanceof HasReferencesToOtherSheets)) {
$sheet->disconnect();
} else {
$sheetsToDisconnect[] = $sheet;
}
}
}
foreach ($sheetsToDisconnect as $sheet) {
$sheet->disconnect();
}
});
$this->afterImport($import);
} catch (Throwable $e) {
$this->raise(new ImportFailed($e));
$this->garbageCollect();
throw $e;
}
return $this;
}
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string $readerType
* @param string|null $disk
* @return array
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws NoTypeDetectedException
* @throws Exceptions\SheetNotFoundException
*/
public function toArray($import, $filePath, string $readerType = null, string $disk = null): array
{
$this->reader = $this->getReader($import, $filePath, $readerType, $disk);
$this->loadSpreadsheet($import);
$sheets = [];
$sheetsToDisconnect = [];
foreach ($this->sheetImports as $index => $sheetImport) {
$calculatesFormulas = $sheetImport instanceof WithCalculatedFormulas;
$formatData = $sheetImport instanceof WithFormatData;
if ($sheet = $this->getSheet($import, $sheetImport, $index)) {
$sheets[$index] = $sheet->toArray($sheetImport, $sheet->getStartRow($sheetImport), null, $calculatesFormulas, $formatData);
// when using WithCalculatedFormulas we need to keep the sheet until all sheets are imported
if (!($sheetImport instanceof HasReferencesToOtherSheets)) {
$sheet->disconnect();
} else {
$sheetsToDisconnect[] = $sheet;
}
}
}
foreach ($sheetsToDisconnect as $sheet) {
$sheet->disconnect();
}
$this->afterImport($import);
return $sheets;
}
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string $readerType
* @param string|null $disk
* @return Collection
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws NoTypeDetectedException
* @throws Exceptions\SheetNotFoundException
*/
public function toCollection($import, $filePath, string $readerType = null, string $disk = null): Collection
{
$this->reader = $this->getReader($import, $filePath, $readerType, $disk);
$this->loadSpreadsheet($import);
$sheets = new Collection();
$sheetsToDisconnect = [];
foreach ($this->sheetImports as $index => $sheetImport) {
$calculatesFormulas = $sheetImport instanceof WithCalculatedFormulas;
$formatData = $sheetImport instanceof WithFormatData;
if ($sheet = $this->getSheet($import, $sheetImport, $index)) {
$sheets->put($index, $sheet->toCollection($sheetImport, $sheet->getStartRow($sheetImport), null, $calculatesFormulas, $formatData));
// when using WithCalculatedFormulas we need to keep the sheet until all sheets are imported
if (!($sheetImport instanceof HasReferencesToOtherSheets)) {
$sheet->disconnect();
} else {
$sheetsToDisconnect[] = $sheet;
}
}
}
foreach ($sheetsToDisconnect as $sheet) {
$sheet->disconnect();
}
$this->afterImport($import);
return $sheets;
}
/**
* @return Spreadsheet
*/
public function getDelegate()
{
return $this->spreadsheet;
}
/**
* @return $this
*/
public function setDefaultValueBinder(): self
{
Cell::setValueBinder(
app(config('excel.value_binder.default', DefaultValueBinder::class))
);
return $this;
}
/**
* @param object $import
*/
public function loadSpreadsheet($import)
{
$this->sheetImports = $this->buildSheetImports($import);
$this->readSpreadsheet();
// When no multiple sheets, use the main import object
// for each loaded sheet in the spreadsheet
if (!$import instanceof WithMultipleSheets) {
$this->sheetImports = array_fill(0, $this->spreadsheet->getSheetCount(), $import);
}
$this->beforeImport($import);
}
public function readSpreadsheet()
{
$this->spreadsheet = $this->reader->load(
$this->currentFile->getLocalPath()
);
}
/**
* @param object $import
*/
public function beforeImport($import)
{
$this->raise(new BeforeImport($this, $import));
}
/**
* @param object $import
*/
public function afterImport($import)
{
$this->raise(new AfterImport($this, $import));
$this->garbageCollect();
}
/**
* @return IReader
*/
public function getPhpSpreadsheetReader(): IReader
{
return $this->reader;
}
/**
* @param object $import
* @return array
*/
public function getWorksheets($import): array
{
// Csv doesn't have worksheets.
if (!method_exists($this->reader, 'listWorksheetNames')) {
return ['Worksheet' => $import];
}
$worksheets = [];
$worksheetNames = $this->reader->listWorksheetNames($this->currentFile->getLocalPath());
if ($import instanceof WithMultipleSheets) {
$sheetImports = $import->sheets();
foreach ($sheetImports as $index => $sheetImport) {
// Translate index to name.
if (is_numeric($index)) {
$index = $worksheetNames[$index] ?? $index;
}
// Specify with worksheet name should have which import.
$worksheets[$index] = $sheetImport;
}
// Load specific sheets.
if (method_exists($this->reader, 'setLoadSheetsOnly')) {
$this->reader->setLoadSheetsOnly(
collect($worksheetNames)->intersect(array_keys($worksheets))->values()->all()
);
}
} else {
// Each worksheet the same import class.
foreach ($worksheetNames as $name) {
$worksheets[$name] = $import;
}
}
return $worksheets;
}
/**
* @return array
*/
public function getTotalRows(): array
{
$info = $this->reader->listWorksheetInfo($this->currentFile->getLocalPath());
$totalRows = [];
foreach ($info as $sheet) {
$totalRows[$sheet['worksheetName']] = $sheet['totalRows'];
}
return $totalRows;
}
/**
* @param $import
* @param $sheetImport
* @param $index
* @return Sheet|null
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws SheetNotFoundException
*/
protected function getSheet($import, $sheetImport, $index)
{
try {
return Sheet::make($this->spreadsheet, $index);
} catch (SheetNotFoundException $e) {
if ($import instanceof SkipsUnknownSheets) {
$import->onUnknownSheet($index);
return null;
}
if ($sheetImport instanceof SkipsUnknownSheets) {
$sheetImport->onUnknownSheet($index);
return null;
}
throw $e;
}
}
/**
* @param object $import
* @return array
*/
private function buildSheetImports($import): array
{
$sheetImports = [];
if ($import instanceof WithMultipleSheets) {
$sheetImports = $import->sheets();
// When only sheet names are given and the reader has
// an option to load only the selected sheets.
if (
method_exists($this->reader, 'setLoadSheetsOnly')
&& count(array_filter(array_keys($sheetImports), 'is_numeric')) === 0
) {
$this->reader->setLoadSheetsOnly(array_keys($sheetImports));
}
}
return $sheetImports;
}
/**
* @param object $import
* @param string|UploadedFile $filePath
* @param string|null $readerType
* @param string $disk
* @return IReader
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws NoTypeDetectedException
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws InvalidArgumentException
*/
private function getReader($import, $filePath, string $readerType = null, string $disk = null): IReader
{
$shouldQueue = $import instanceof ShouldQueue;
if ($shouldQueue && !$import instanceof WithChunkReading) {
throw new InvalidArgumentException('ShouldQueue is only supported in combination with WithChunkReading.');
}
if ($import instanceof WithEvents) {
$this->registerListeners($import->registerEvents());
}
if ($import instanceof WithCustomValueBinder) {
Cell::setValueBinder($import);
}
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
$temporaryFile = $shouldQueue ? $this->temporaryFileFactory->make($fileExtension) : $this->temporaryFileFactory->makeLocal(null, $fileExtension);
$this->currentFile = $temporaryFile->copyFrom(
$filePath,
$disk
);
return ReaderFactory::make(
$import,
$this->currentFile,
$readerType
);
}
/**
* Garbage collect.
*/
private function garbageCollect()
{
$this->clearListeners();
$this->setDefaultValueBinder();
// Force garbage collecting
unset($this->sheetImports, $this->spreadsheet);
$this->currentFile->delete();
}
}