ajout SsReader et CsvReader
This commit is contained in:
parent
291b478d93
commit
6bffe89904
|
@ -1,10 +1,31 @@
|
||||||
<?php
|
<?php
|
||||||
namespace nur\sery\ext\spreadsheet;
|
namespace nur\sery\ext\spreadsheet;
|
||||||
|
|
||||||
|
use nur\sery\ValueException;
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
class wsutils {
|
class wsutils {
|
||||||
|
static function get_ws(?string $wsname, Spreadsheet $ss, bool $create=false): ?Worksheet {
|
||||||
|
if ($wsname == null) {
|
||||||
|
$ws = $ss->getActiveSheet();
|
||||||
|
} elseif (is_numeric($wsname)) {
|
||||||
|
$sheetCount = $ss->getSheetCount();
|
||||||
|
if ($wsname < 1 || $wsname > $sheetCount) {
|
||||||
|
throw ValueException::invalid_value($wsname, "sheet index");
|
||||||
|
}
|
||||||
|
$ws = $ss->getSheet($wsname - 1);
|
||||||
|
} else {
|
||||||
|
$ws = $ss->getSheetByName($wsname);
|
||||||
|
if ($ws === null) {
|
||||||
|
if ($create) $ws = $ss->createSheet()->setTitle($wsname);
|
||||||
|
else throw ValueException::invalid_value($wsname, "sheet name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ws;
|
||||||
|
}
|
||||||
|
|
||||||
static function get_highest_coords(Worksheet $ws): array {
|
static function get_highest_coords(Worksheet $ws): array {
|
||||||
$highestColumnA = $ws->getHighestColumn();
|
$highestColumnA = $ws->getHighestColumn();
|
||||||
$highestCol = Coordinate::columnIndexFromString($highestColumnA);
|
$highestCol = Coordinate::columnIndexFromString($highestColumnA);
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\file\csv;
|
||||||
|
|
||||||
|
use IteratorAggregate;
|
||||||
|
|
||||||
|
abstract class AbstractReader implements IReader {
|
||||||
|
const SCHEMA = null;
|
||||||
|
|
||||||
|
const HEADERS = null;
|
||||||
|
|
||||||
|
/** @var ?string nom du fichier depuis lequel lire */
|
||||||
|
const INPUT = null;
|
||||||
|
|
||||||
|
function __construct($input, ?array $params=null) {
|
||||||
|
if ($input !== null) $params["input"] = $input;
|
||||||
|
#
|
||||||
|
$this->schema = $params["schema"] ?? static::SCHEMA;
|
||||||
|
$this->headers = $params["headers"] ?? static::HEADERS;
|
||||||
|
$this->input = $params["input"] ?? static::INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ?array $schema;
|
||||||
|
|
||||||
|
protected ?array $headers;
|
||||||
|
|
||||||
|
protected $input;
|
||||||
|
|
||||||
|
protected int $isrc = 0, $idest = 0;
|
||||||
|
|
||||||
|
protected function cook(array &$row): bool {
|
||||||
|
if ($this->isrc == 0) {
|
||||||
|
# ligne d'en-tête
|
||||||
|
$headers = $this->headers;
|
||||||
|
if ($headers === null) {
|
||||||
|
if ($this->schema === null) $headers = null;
|
||||||
|
else $headers = array_keys($this->schema);
|
||||||
|
if ($headers === null) $headers = $row;
|
||||||
|
$this->headers = $headers;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$row = array_combine($this->headers, $row);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\file\csv;
|
||||||
|
|
||||||
|
use nur\sery\file\FileReader;
|
||||||
|
use nur\sery\php\time\Date;
|
||||||
|
use nur\sery\php\time\DateTime;
|
||||||
|
|
||||||
|
class CsvReader extends AbstractReader {
|
||||||
|
function getIterator() {
|
||||||
|
$reader = new FileReader($this->input);
|
||||||
|
while (($row = $reader->fgetcsv()) !== null) {
|
||||||
|
foreach ($row as &$col) {
|
||||||
|
# conversion Date et DateTime
|
||||||
|
if (DateTime::isa_datetime($col, true)) {
|
||||||
|
$col = new DateTime($col);
|
||||||
|
} elseif (DateTime::isa_date($col, true)) {
|
||||||
|
$col = new Date($col);
|
||||||
|
}
|
||||||
|
}; unset($col);
|
||||||
|
|
||||||
|
if ($this->cook($row)) {
|
||||||
|
yield $row;
|
||||||
|
$this->idest++;
|
||||||
|
}
|
||||||
|
$this->isrc++;
|
||||||
|
}
|
||||||
|
$reader->close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\file\csv;
|
||||||
|
|
||||||
|
use IteratorAggregate;
|
||||||
|
|
||||||
|
interface IReader extends IteratorAggregate {
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ namespace nur\sery\file\csv;
|
||||||
|
|
||||||
use nur\sery\ext\spreadsheet\wsutils;
|
use nur\sery\ext\spreadsheet\wsutils;
|
||||||
use nur\sery\os\path;
|
use nur\sery\os\path;
|
||||||
|
use nur\sery\ValueException;
|
||||||
use nur\sery\web\http;
|
use nur\sery\web\http;
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
|
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
|
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
|
||||||
|
@ -19,11 +20,14 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||||
class SsBuilder extends AbstractBuilder {
|
class SsBuilder extends AbstractBuilder {
|
||||||
use TAbstractBuilder;
|
use TAbstractBuilder;
|
||||||
|
|
||||||
|
/** @var ?string nom de la feuille dans laquelle écrire */
|
||||||
|
const WSNAME = null;
|
||||||
|
|
||||||
function __construct(?string $output, ?array $params=null) {
|
function __construct(?string $output, ?array $params=null) {
|
||||||
parent::__construct($output, $params);
|
parent::__construct($output, $params);
|
||||||
$this->ss = new Spreadsheet();
|
$this->ss = new Spreadsheet();
|
||||||
$this->valueBinder = new StringValueBinder();
|
$this->valueBinder = new StringValueBinder();
|
||||||
$this->setWsname($params["wsname"] ?? null);
|
$this->setWsname($params["wsname"] ?? static::WSNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Spreadsheet $ss;
|
protected Spreadsheet $ss;
|
||||||
|
@ -45,6 +49,12 @@ class SsBuilder extends AbstractBuilder {
|
||||||
$this->rowStyle = self::STYLE_ROW;
|
$this->rowStyle = self::STYLE_ROW;
|
||||||
if ($wsname === null) {
|
if ($wsname === null) {
|
||||||
$ws = $ss->getActiveSheet();
|
$ws = $ss->getActiveSheet();
|
||||||
|
} elseif (is_numeric($wsname)) {
|
||||||
|
$sheetCount = $ss->getSheetCount();
|
||||||
|
if ($wsname < 1 || $wsname > $sheetCount) {
|
||||||
|
throw ValueException::invalid_value($wsname, "sheet index");
|
||||||
|
}
|
||||||
|
$ws = $ss->getSheet($wsname - 1);
|
||||||
} else {
|
} else {
|
||||||
$ws = $ss->getSheetByName($wsname);
|
$ws = $ss->getSheetByName($wsname);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\file\csv;
|
||||||
|
|
||||||
|
use nur\sery\cl;
|
||||||
|
use nur\sery\ext\spreadsheet\wsutils;
|
||||||
|
use nur\sery\file\web\Upload;
|
||||||
|
use nur\sery\php\time\Date;
|
||||||
|
use nur\sery\php\time\DateTime;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||||
|
|
||||||
|
class SsReader extends AbstractReader {
|
||||||
|
static function with(Upload $upload): IReader {
|
||||||
|
if ($upload->isExt(".csv")) {
|
||||||
|
return new CsvReader($upload->tmpName);
|
||||||
|
} else {
|
||||||
|
return new static($upload->tmpName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DATETIME_FORMAT = 'dd/mm/yyyy hh:mm:ss';
|
||||||
|
const DATE_FORMAT = 'dd/mm/yyyy';
|
||||||
|
const TIME_FORMAT = 'hh:mm:ss';
|
||||||
|
const FORMAT_MAPPINGS = [
|
||||||
|
'mm/dd hh' => self::DATETIME_FORMAT,
|
||||||
|
'dd/mm hh' => self::DATETIME_FORMAT,
|
||||||
|
'mm/dd hh:mm' => self::DATETIME_FORMAT,
|
||||||
|
'dd/mm hh:mm' => self::DATETIME_FORMAT,
|
||||||
|
'mm/dd hh:mm:ss' => self::DATETIME_FORMAT,
|
||||||
|
'dd/mm hh:mm:ss' => self::DATETIME_FORMAT,
|
||||||
|
'mm/dd/yyyy hh' => self::DATETIME_FORMAT,
|
||||||
|
'dd/mm/yyyy hh' => self::DATETIME_FORMAT,
|
||||||
|
'mm/dd/yyyy hh:mm' => self::DATETIME_FORMAT,
|
||||||
|
'dd/mm/yyyy hh:mm' => self::DATETIME_FORMAT,
|
||||||
|
'mm/dd/yyyy hh:mm:ss' => self::DATETIME_FORMAT,
|
||||||
|
'dd/mm/yyyy hh:mm:ss' => self::DATETIME_FORMAT,
|
||||||
|
'yyyy/mm/dd hh' => self::DATETIME_FORMAT,
|
||||||
|
'yyyy/mm/dd hh:mm' => self::DATETIME_FORMAT,
|
||||||
|
'yyyy/mm/dd hh:mm:ss' => self::DATETIME_FORMAT,
|
||||||
|
|
||||||
|
'mm/dd' => self::DATE_FORMAT,
|
||||||
|
'dd/mm' => self::DATE_FORMAT,
|
||||||
|
'mm/dd/yyyy' => self::DATE_FORMAT,
|
||||||
|
'dd/mm/yyyy' => self::DATE_FORMAT,
|
||||||
|
'yyyy/mm/dd' => self::DATE_FORMAT,
|
||||||
|
'mm/yyyy' => self::DATE_FORMAT,
|
||||||
|
|
||||||
|
'hh AM/PM' => self::TIME_FORMAT,
|
||||||
|
'hh:mm AM/PM' => self::TIME_FORMAT,
|
||||||
|
'hh:mm:ss AM/PM' => self::TIME_FORMAT,
|
||||||
|
'hh' => self::TIME_FORMAT,
|
||||||
|
'hh:mm' => self::TIME_FORMAT,
|
||||||
|
'hh:mm:ss' => self::TIME_FORMAT,
|
||||||
|
'[hh]:mm:ss' => self::TIME_FORMAT,
|
||||||
|
'mm:ss' => self::TIME_FORMAT,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var ?string nom de la feuille depuis laquelle lire */
|
||||||
|
const WSNAME = null;
|
||||||
|
|
||||||
|
function __construct($input, ?array $params=null) {
|
||||||
|
parent::__construct($input, $params);
|
||||||
|
$this->wsname = $params["wsname"] ?? static::WSNAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ?string $wsname;
|
||||||
|
|
||||||
|
function getIterator() {
|
||||||
|
$ss = IOFactory::load($this->input);
|
||||||
|
$ws = wsutils::get_ws($this->wsname, $ss);
|
||||||
|
|
||||||
|
[$nbCols, $nbRows] = wsutils::compute_max_coords($ws);
|
||||||
|
$this->isrc = $this->idest = 0;
|
||||||
|
for ($nrow = 1; $nrow <= $nbRows; $nrow++) {
|
||||||
|
$row = [];
|
||||||
|
for ($ncol = 1; $ncol <= $nbCols; $ncol++) {
|
||||||
|
if ($ws->cellExistsByColumnAndRow($ncol, $nrow)) {
|
||||||
|
$cell = $ws->getCellByColumnAndRow($ncol, $nrow);
|
||||||
|
$value = $cell->getValue();
|
||||||
|
if ($value instanceof RichText) {
|
||||||
|
$value = $value->getPlainText();
|
||||||
|
} else {
|
||||||
|
$dataType = $cell->getDataType();
|
||||||
|
if ($dataType == DataType::TYPE_NUMERIC || $dataType == DataType::TYPE_FORMULA) {
|
||||||
|
# si c'est un format date, le forcer à une valeur standard
|
||||||
|
$origFormatCode = $cell->getStyle()->getNumberFormat()->getFormatCode();
|
||||||
|
if (strpbrk($origFormatCode, "ymdhs") !== false) {
|
||||||
|
$formatCode = $origFormatCode;
|
||||||
|
$formatCode = preg_replace('/y+/', "yyyy", $formatCode);
|
||||||
|
$formatCode = preg_replace('/m+/', "mm", $formatCode);
|
||||||
|
$formatCode = preg_replace('/d+/', "dd", $formatCode);
|
||||||
|
$formatCode = preg_replace('/h+/', "hh", $formatCode);
|
||||||
|
$formatCode = preg_replace('/s+/', "ss", $formatCode);
|
||||||
|
$formatCode = preg_replace('/-+/', "/", $formatCode);
|
||||||
|
$formatCode = preg_replace('/\\\\ /', " ", $formatCode);
|
||||||
|
$formatCode = preg_replace('/;@$/', "", $formatCode);
|
||||||
|
$formatCode = cl::get(self::FORMAT_MAPPINGS, $formatCode, $formatCode);
|
||||||
|
if ($formatCode !== $origFormatCode) {
|
||||||
|
$cell->getStyle()->getNumberFormat()->setFormatCode($formatCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$value = $cell->getFormattedValue();
|
||||||
|
# conversion Date et DateTime
|
||||||
|
if (DateTime::isa_datetime($value, true)) {
|
||||||
|
$value = new DateTime($value);
|
||||||
|
} elseif (DateTime::isa_date($value, true)) {
|
||||||
|
$value = new Date($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
$row[] = $value;
|
||||||
|
}
|
||||||
|
if ($this->cook($row)) {
|
||||||
|
yield $row;
|
||||||
|
$this->idest++;
|
||||||
|
}
|
||||||
|
$this->isrc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue