Repository: AnourValar/office
Branch: master
Commit: 899ec9cc1ecb
Files: 37
Total size: 401.4 KB
Directory structure:
gitextract_73d96ymn/
├── .gitattributes
├── .gitignore
├── .php-cs-fixer.php
├── LICENSE
├── README.md
├── composer.json
├── phpcs.xml
├── phpstan.neon
├── phpunit.xml
├── psalm.xml
├── src/
│ ├── Buffer.php
│ ├── DocumentService.php
│ ├── Drivers/
│ │ ├── DocumentInterface.php
│ │ ├── GridInterface.php
│ │ ├── LoadInterface.php
│ │ ├── MixInterface.php
│ │ ├── MultiSheetInterface.php
│ │ ├── PhpSpreadsheetDriver.php
│ │ ├── SaveInterface.php
│ │ ├── SheetsInterface.php
│ │ └── ZipDriver.php
│ ├── Facades/
│ │ ├── ExportGridInterface.php
│ │ ├── ExportGridQueryInterface.php
│ │ └── ExportService.php
│ ├── Format.php
│ ├── Generated.php
│ ├── GridService.php
│ ├── Mixer.php
│ ├── Sheets/
│ │ ├── Parser.php
│ │ └── SchemaMapper.php
│ ├── SheetsService.php
│ ├── Traits/
│ │ ├── Parser.php
│ │ └── XFormat.php
│ └── resources/
│ └── grid.xlsx
└── tests/
├── GridServiceTest.php
├── SheetsParserTest.php
└── TraitsTest.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
/tests export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.php-cs-fixer.php export-ignore
phpcs.xml export-ignore
phpstan.neon export-ignore
phpunit.xml export-ignore
psalm.xml export-ignore
================================================
FILE: .gitignore
================================================
.phpunit.cache/
vendor/
composer.lock
.php-cs-fixer.cache
================================================
FILE: .php-cs-fixer.php
================================================
exclude('vendor')
->in(__DIR__);
$config = new PhpCsFixer\Config();
return $config->setRules([
'@PSR12' => true,
])
->setFinder($finder);
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 AnourValar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Office: Documents | Reports | Grids
## Installation
### Minimal
```bash
composer require anourvalar/office
```
### Phpspreadsheet is required to work with Excel (xlsx).
```bash
composer require phpoffice/phpspreadsheet "^3.10"
```
### Zipstream-php is required to work with Word (docx).
```bash
composer require maennchen/zipstream-php "^3.2"
```
### Mpdf is required to work with PDF.
```bash
composer require mpdf/mpdf: "^8.1"
```
## Generate a document from an XLSX (Excel) template
### One-dimensional table (basic usage)
**template1.xlsx:**

```php
$data = [
// scalar
'vat' => 'No',
'total' => [
'price' => 2004.14,
'qty' => 3,
],
// one-dimensional table
'products' => [
[
'name' => 'Product #1',
'price' => 989,
'qty' => 1,
'date' => new \DateTime('2022-03-30'),
],
[
'name' => 'Product #2',
'price' => 1015.14,
'qty' => 2,
'date' => new \DateTime('2022-03-31'),
],
],
];
// Save to the file
(new \AnourValar\Office\SheetsService())
->generate(
'template1.xlsx', // template filename
$data // markers
)
->saveAs(
'generated_document.xlsx', // filename
\AnourValar\Office\Format::Xlsx // save format
);
// Output to the browser
header('Content-type: ' . \AnourValar\Office\Format::Xlsx->contentType());
header('Content-Disposition: attachment; filename="generated_document.xlsx"');
echo (new \AnourValar\Office\SheetsService())
->generate('template1.xlsx', $data)
->save(\AnourValar\Office\Format::Xlsx);
// Available formats:
// \AnourValar\Office\Format::Xlsx
// \AnourValar\Office\Format::Pdf
// \AnourValar\Office\Format::Html
// \AnourValar\Office\Format::Ods
```
**generated_document.xlsx:**

**The same template with empty data**

### Two-dimensional table
**template2.xlsx:**

```php
$data = [
'best_manager' => 'Sveta',
// two-dimensional table
'managers' => [
'titles' => [[ 'William', 'James', 'Sveta' ]],
'values' => [
[ // additional row
'month' => 'January',
'amount' => [700, 800, 900], // additional columns
],
[
'month' => 'February',
'amount' => [7000, 8000, 9000],
],
[
'month' => 'March',
'amount' => [70000, 80000, 90000],
],
],
],
];
// Save as XLSX (Excel)
(new \AnourValar\Office\SheetsService())
->generate('template2.xlsx', $data)
->saveAs('generated_document.xlsx'); // second argument (format) is optional
```
**generated_document.xlsx:**

### Additional Features
**template3.xlsx:**

```php
$data = [
'foo' => 'Hello',
'bar' => function (SheetsInterface $driver, $column, $row) {
$driver->insertImage('logo.png', $cell, ['width' => 100, 'offset_y' => -45]);
return 'Logo!'; // replace marker "[bar]" with "Logo!"
}
];
(new \AnourValar\Office\SheetsService())
->hookValue(function (SheetsInterface $driver, $column, $row, $value, $sheetIndex) {
// Hook will be called for every cell which is changing
$value .= ' world';
return $value;
})
->generate(
'template3.ods', // ods template
$data,
true // cells auto format instead of template setup
)
->saveAs('generated_document.xlsx');
// Available hooks:
// hookLoad: Closure(SheetsInterface $driver, string $templateFile, Format $templateFormat)
// hookBefore: Closure(SheetsInterface $driver, array &$data)
// hookValue: Closure(SheetsInterface $driver, string $column, int $row, $value, int $sheetIndex)
// hookAfter: Closure(SheetsInterface $driver)
```
**generated_document.xlsx:**

### Dynamic templates
```php
$data = [
'group1' => [
'name' => 'Group 1',
'products' => [
['name' => 'Product 1', 'stock' => 101],
['name' => 'Product 2', 'stock' => 102],
],
],
'group2' => [
'name' => 'Group 2',
'products' => [
['name' => 'Product 3', 'stock' => 103],
['name' => 'Product 4', 'stock' => 104],
],
],
];
(new \AnourValar\Office\SheetsService())
->hookLoad(function ($driver, string $templateFile, $templateFormat) {
// create empty document instead of using existing
return $driver->create();
})
->hookBefore(function ($driver, array &$data) {
// place markers on-fly
$row = 1;
foreach (array_keys($data) as $group) {
// group's title
$driver
->setValue("A$row", "[{$group}.name]")
->mergeCells("A$row:B$row")
->setStyle("A$row", ['align' => 'center', 'bold' => true]);
$row++;
// group's products
$driver
->setValue("A$row", "[$group.products.name]")
->setValue("B$row", "[$group.products.stock]");
$row++;
}
})
->generate('', $data)
->saveAs('generated_document.xlsx');
```
**Dynamic template overview**

**generated_document.xlsx:**

### Merge (union) few documents to a single file
```php
$dataA = ['foo' => 'hello'];
$dataB = ['foo' => 'world'];
$documentA = (new \AnourValar\Office\SheetsService())->generate('template.xlsx', $dataA);
$documentB = (new \AnourValar\Office\SheetsService())->generate('template.xlsx', $dataB);
$mixer = new \AnourValar\Office\Mixer();
$mixer($documentA, $documentB)->saveAs('generated_document.xlsx');
```
### Access the PhpSpreadsheet directly (default driver)
```php
(new \AnourValar\Office\SheetsService())
->hookBefore(function (\AnourValar\Office\Drivers\PhpSpreadsheetDriver $driver, array &$data) {
$spreadsheet = $driver->spreadsheet;
// @see \PhpOffice\PhpSpreadsheet\Spreadsheet
$spreadsheet->createSheet()->setTitle('Foo Bar'); // adding a new Worksheet
})
->generate('template.xlsx', [])
->saveAs('generated_document.xlsx');
```
## Generate a document from an DOCX (Word) template
```php
(new \AnourValar\Office\DocumentService)
->generate('template.docx', ['foo' => 'bar'])
->saveAs('generated_document.docx');
```
**template.docx:**

**generated_document.docx:**

## Export table (Grid)
### Simple usage
```php
$data = [
['William', 3000],
['James', 4000],
['Sveta', 5000],
];
// Save as XLSX (Excel)
(new \AnourValar\Office\GridService())
->generate(
['Name', 'Sales'], // headers
$data // data
)
->saveAs('generated_grid.xlsx');
```
**generated_grid.xlsx:**

### Advanced usage (generators)
```php
$headers = [
['title' => 'Name', 'width' => 30],
['title' => 'Sales'],
];
$data = function () {
yield ['name' => 'William', 'sales' => 3000];
yield ['name' => 'James', 'sales' => 4000];
yield ['name' => 'Sveta', 'sales' => 5000];
};
// Save as XLSX (Excel)
(new \AnourValar\Office\GridService())
->hookHeader(function (GridInterface $driver, mixed $header, $key, $column) {
if (isset($header['width'])) {
$driver->setWidth($column, $header['width']); // column with fixed width
} else {
$driver->autoWidth($column); // column with auto width
}
return $header['title'];
})
->hookRow(function (GridInterface $driver, mixed $row, $key) {
return [
$row['name'],
$row['sales'],
];
})
->hookAfter(function (
GridInterface $driver,
string $headersRange,
string $dataRange,
string $totalRange,
array $columns
) {
$driver->setSheetTitle('Foo');
$driver->setStyle(
$headersRange, // A1:B1
['bold' => true, 'background_color' => 'EEEEEE']
);
$driver->setStyle(
$totalRange, // A1:B4
['borders' => true, 'align' => 'left']
);
})
->generate($headers, $data)
->saveAs('generated_grid.xlsx');
```
**generated_grid.xlsx:**

### Performance
By default, GridService uses PhpSpreadsheetDriver which gives a lot of features and flexability.
The only cons are performance and memory consumtion.
ZipDriver as an alternative is simpler, but much more faster:
```bash
composer require maennchen/zipstream-php "^3.2"
```
```php
$data = [
['William', 3000],
['James', 4000],
['Sveta', 5000],
];
// Save as XLSX (Excel)
(new \AnourValar\Office\GridService(new \AnourValar\Office\Drivers\ZipDriver()))
->generate(['Name', 'Sales'], $data)
->saveAs('generated_grid.xlsx');
```
================================================
FILE: composer.json
================================================
{
"name": "anourvalar/office",
"description": "Generate documents from existing Excel & Word templates | Export tables to Excel (Grids)",
"keywords": [
"excel", "xls", "xlsx", "template", "view", "document", "contract", "report", "generate", "engine", "fill",
"markers", "replacers", "placeholder", "templater", "pdf", "ods", "grid", "export", "generator", "anourvalar",
"variables", "word", "doc", "docx", "table"
],
"homepage": "https://github.com/AnourValar/office",
"license": "MIT",
"require": {
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^11.0",
"phpstan/phpstan": "^2.0",
"friendsofphp/php-cs-fixer": "^3.26",
"squizlabs/php_codesniffer": "^3.7",
"vimeo/psalm": "^7.0"
},
"autoload": {
"psr-4": {"AnourValar\\Office\\": "src/"}
},
"autoload-dev": {
"psr-4": {"AnourValar\\Office\\Tests\\": "tests/"}
}
}
================================================
FILE: phpcs.xml
================================================
PHPCS ruleset
src
tests
src/resources
tests/
tests/
================================================
FILE: phpstan.neon
================================================
parameters:
paths:
- src
- tests
# The level 10 is the highest level
level: 5
ignoreErrors:
- '#has an uninitialized readonly property#'
- '#Binary operation \"\-\" between non\-empty\-string#'
- '#Call to an undefined method AnourValar\\Office\\Drivers\\SaveInterface\:\:getSheetCount\(\)#'
- '#Call to an undefined method AnourValar\\Office\\Drivers\\SaveInterface\:\:replace\(\)#'
- '#unknown class PhpOffice#'
- '#Class PhpOffice\\PhpSpreadsheet\\Writer\\Csv not found#'
- '#Unsafe usage of new static#'
- '#Call to an undefined method AnourValar\\Office\\Drivers\\SaveInterface\:\:setGrid\(\)#'
- '#Parameter \#1 \$driver of method AnourValar\\Office\\GridService\:\:getGenerator\(\) expects AnourValar\\Office\\Drivers\\GridInterface#'
- '#Class AnourValar\\Office\\Tests\\SheetsParserTest has an uninitialized property \$service#'
- '#has an uninitialized property \$fileSystem#'
- '#\(\) on iterable\.#'
- '#Instantiated class ZipStream#'
- '#unknown class ZipStream#'
- '#has an uninitialized property \$sourceActiveSheetIndex#'
- '#\$format is assigned outside of the constructor#'
- '#has invalid return type PhpOffice#'
- '#\$spreadsheet is assigned outside of the constructor#'
- '#Binary operation \"\+\" between non\-empty\-string#'
- '#Instantiated class PhpOffice#'
- '#expects string, int given#'
- '#has invalid type PhpOffice#'
- '#Match expression does not handle remaining value: mixed#'
- '#Access to an undefined property AnourValar\\Office\\Drivers\\MixInterface\:\:\$spreadsheet#'
- '#Call to an undefined method AnourValar\\Office\\Drivers\\MixInterface\:\:sheet\(\)#'
- '#Call to an undefined method#'
- '#Offset numeric\-string on list\ in isset\(\) does not exist\.#'
- '#SaveInterface given\.#'
- '#has invalid return type Illuminate#'
excludePaths:
checkFunctionNameCase: true
checkInternalClassCaseSensitivity: true
reportMaybesInMethodSignatures: true
reportStaticMethodSignatures: true
checkUninitializedProperties: true
checkDynamicProperties: true
reportAlwaysTrueInLastCondition: true
reportWrongPhpDocTypeInVarTag: true
checkMissingCallableSignature: true
reportPossiblyNonexistentGeneralArrayOffset: true
reportPossiblyNonexistentConstantArrayOffset: true
reportAnyTypeWideningInVarTag: true
================================================
FILE: phpunit.xml
================================================
tests
src/
================================================
FILE: psalm.xml
================================================
================================================
FILE: src/Buffer.php
================================================
resource = tmpfile();
fwrite($this->resource, $buffer);
$this->filename = stream_get_meta_data($this->resource)['uri'];
}
/**
* @see magic
*
* @return string
*/
public function __toString(): string
{
return $this->filename;
}
/**
* @return void
* @psalm-suppress InaccessibleProperty
*/
public function __destruct()
{
fclose($this->resource); // works with php-fpm, octane, queue
}
}
================================================
FILE: src/DocumentService.php
================================================
driver = $driver;
}
/**
* Generate a document from the template (document)
*
* @param string|\Stringable $templateFile
* @param mixed $data
* @return \AnourValar\Office\Generated
*/
public function generate(string|\Stringable $templateFile, mixed $data): Generated
{
// Handle with input data
$data = $this->canonizeData($data);
// Open the template
$templateFormat = Format::tryFrom(mb_strtolower(pathinfo($templateFile, PATHINFO_EXTENSION))) ?? Format::Docx;
$driver = $this->driver->load($templateFile, $templateFormat);
// Handle
$driver->replace($data);
// Return
return new Generated($driver);
}
/**
* @param mixed $data
* @return array
*/
protected function canonizeData(mixed $data): array
{
$result = [];
if (is_object($data) && method_exists($data, 'toArray')) {
$data = $data->toArray();
}
foreach ($this->dot($data) as $key => $value) {
$result["[$key]"] = $value;
}
return $result;
}
}
================================================
FILE: src/Drivers/DocumentInterface.php
================================================
spreadsheet->getActiveSheet();
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\GridInterface::create()
* @psalm-suppress InaccessibleProperty
*/
public function create(): self
{
$instance = new static();
$instance->spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$instance->sourceActiveSheetIndex = 0;
$this->readConfiguration($instance);
return $instance;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\LoadInterface::load()
* @psalm-suppress InaccessibleProperty
*/
public function load(string $file, \AnourValar\Office\Format $format): self
{
$instance = new static();
$instance->spreadsheet = IOFactory::createReader($instance->getFormat($format))->load($file);
$instance->sourceActiveSheetIndex = $instance->spreadsheet->getActiveSheetIndex();
$this->readConfiguration($instance);
return $instance;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SaveInterface::save()
*/
public function save(string $file, \AnourValar\Office\Format $format): void
{
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($this->spreadsheet, $this->getFormat($format));
$this->writeConfiguration($writer);
$count = $this->spreadsheet->getSheetCount();
for ($i = 0; $i < $count; $i++) {
$this->spreadsheet->getSheet($i)->setSelectedCells('A1');
}
$this->spreadsheet->setActiveSheetIndex($this->sourceActiveSheetIndex);
if (method_exists($writer, 'writeAllSheets')) {
$writer->writeAllSheets();
}
$writer->save($file);
}
/**
* Clean up
*
* @return void
*/
public function __destruct()
{
if (isset($this->spreadsheet)) {
$this->spreadsheet->disconnectWorksheets();
gc_collect_cycles();
}
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\MultiSheetInterface::setSheet()
*/
public function setSheet(int $index): self
{
$this->spreadsheet->setActiveSheetIndex($index);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\MultiSheetInterface::getSheetCount()
*/
public function getSheetCount(): int
{
return $this->spreadsheet->getSheetCount();
}
/**
* Apply value to a cell
*
* @param string $cell
* @param mixed $value
* @param bool $autoCellFormat
* @return self
*/
public function setValue(string $cell, $value, bool $autoCellFormat = true): self
{
if ($value instanceof \DateTimeInterface) {
$this->sheet()->setCellValue($cell, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($value));
if ($autoCellFormat) {
$this->setCellFormat($cell, static::FORMAT_DATE);
}
} elseif (is_string($value) || is_null($value)) {
if (is_numeric($value)) {
$this->sheet()->getCell($cell)->setValueExplicit($value, DataType::TYPE_STRING);
} else {
$this->sheet()->setCellValue($cell, $value);
}
} else {
if ($autoCellFormat && is_double($value)) {
$this->setCellFormat($cell, static::FORMAT_DOUBLE);
} elseif ($autoCellFormat && is_integer($value)) {
$this->setCellFormat($cell, static::FORMAT_INT);
}
$this->sheet()->getCell($cell)->setValueExplicit($value, DataType::TYPE_NUMERIC);
}
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::setValues()
*/
public function setValues(array $data, bool $autoCellFormat = true): self
{
foreach ($data as $row => $columns) {
foreach ($columns as $column => $value) {
$this->setValue($column.$row, $value, $autoCellFormat);
}
}
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\GridInterface::setGrid()
*/
public function setGrid(iterable $data): self
{
$row = 0;
foreach ($data as $values) {
$row++;
$column = 'A';
foreach ($values as $value) {
if ($value !== '' && $value !== null) {
$this->setValue($column.$row, $value);
}
$column = $this->strIncrement($column);
}
}
return $this;
}
/**
* Get cell' value
*
* @param string $cell
* @return mixed
*/
public function getValue(string $cell)
{
return $this->sheet()->getCell($cell)->getValue();
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::getValues()
*/
public function getValues(?string $ceilRange): array
{
if (! $ceilRange) {
$ceilRange = sprintf('A1:%s%s', $this->sheet()->getHighestColumn(), $this->sheet()->getHighestRow());
}
return $this->sheet()->rangeToArray(
$ceilRange, // The worksheet range that we want to retrieve
null, // Value that should be returned for empty cells
false, // Should formulas be calculated (the equivalent of getCalculatedValue() for each cell)
false, // Should values be formatted (the equivalent of getFormattedValue() for each cell)
true // Should the array be indexed by cell row and cell column
);
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::getMergeCells()
*/
public function getMergeCells(): array
{
return array_values($this->sheet()->getMergeCells());
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::mergeCells()
*/
public function mergeCells(string $ceilRange): self
{
$this->sheet()->mergeCells($ceilRange);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::copyStyle()
*/
public function copyStyle(string $cellFrom, string $rangeTo): self
{
$this->sheet()->duplicateStyle($this->sheet()->getStyle($cellFrom), $rangeTo);
if ($conditionalStyle = $this->sheet()->getConditionalStyles($cellFrom)) {
$this->sheet()->duplicateConditionalStyle($conditionalStyle, $rangeTo);
}
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::copyCellFormat()
*/
public function copyCellFormat(string $cellFrom, string $rangeTo): self
{
$this->setCellFormat($rangeTo, $this->sheet()->getStyle($cellFrom)->getNumberFormat()->getFormatCode());
return $this;
}
/**
* Set cell (data) format
*
* @param string $range
* @param string $format
* @return self
*/
public function setCellFormat(string $range, string $format): self
{
$this->sheet()->getStyle($range)->getNumberFormat()->setFormatCode($format);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::addRow()
*/
public function addRow(int $rowBefore, int $qty = 1): self
{
$this->sheet()->insertNewRowBefore($rowBefore, $qty);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::deleteRow()
*/
public function deleteRow(int $row, int $qty = 1): self
{
foreach ($this->getMergeCells() as $merge) {
preg_match('#(\d+)#', $merge, $details);
if ($details[1] == $row) {
$this->sheet()->unmergeCells($merge);
}
}
$this->sheet()->removeRow($row, $qty);
return $this;
}
/**
* Add a column
*
* @param string $columnBefore
* @param int $qty
* @return self
*/
public function addColumn(string $columnBefore, int $qty = 1): self
{
$this->sheet()->insertNewColumnBefore($columnBefore, $qty);
return $this;
}
/**
* Set auto-width for a column
*
* @param string $column
* @return self
*/
public function autoWidth(string $column): self
{
$this->sheet()->getColumnDimension($column)->setAutoSize(true);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SheetsInterface::copyWidth()
*/
public function copyWidth(string $columnFrom, string $columnTo): self
{
$width = $this->sheet()->getColumnDimension($columnFrom)->getWidth();
$this->setWidth($columnTo, $width);
return $this;
}
/**
* Set fixed width for a column
*
* @param string $column
* @param int|float $width
* @return self
*/
public function setWidth(string $column, int|float $width): self
{
$this->sheet()->getColumnDimension($column)->setWidth($width);
return $this;
}
/**
* Copy row's height
*
* @param int $rowFrom
* @param int $rowTo
* @return self
*/
public function copyHeight(int $rowFrom, int $rowTo): self
{
$height = $this->sheet()->getRowDimension($rowFrom)->getRowHeight();
$this->setHeight($rowTo, $height);
return $this;
}
/**
* Set fixed height for a row
*
* @param string $row
* @param int|float $height
* @return self
*/
public function setHeight(string $row, int|float $height): self
{
$this->sheet()->getRowDimension($row)->setRowHeight($height);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\MixInterface::setSheetTitle()
*/
public function setSheetTitle(string $title): self
{
$this->sheet()->setTitle($title);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\MixInterface::getSheetTitle()
*/
public function getSheetTitle(): string
{
return $this->sheet()->getTitle();
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\MixInterface::mergeDriver()
*/
public function mergeDriver(\AnourValar\Office\Drivers\MixInterface $driver): self
{
$index = $driver->spreadsheet->getActiveSheetIndex();
$this->spreadsheet->addExternalSheet($driver->sheet());
$driver->spreadsheet->createSheet($index);
return $this;
}
/**
* Apply cell`s style without format
*
* @param string $cellFrom
* @param string $rangeTo
* @param bool $copyAlignment
* @return self
*/
public function copyStyleWithoutFormat(string $cellFrom, string $rangeTo, bool $copyAlignment = false): self
{
$style = $this->sheet()->getStyle($cellFrom)->exportArray();
if (! $copyAlignment) {
unset($style['alignment'], $style['numberFormat'], $style['protection']);
} else {
unset($style['numberFormat'], $style['protection']);
}
// @TODO: fixed ?
if (
! isset($style['borders']['allBorders'])
&& isset($style['borders']['bottom'], $style['borders']['top'])
&& isset($style['borders']['left'], $style['borders']['right'])
&& $style['borders']['bottom'] == $style['borders']['top']
&& $style['borders']['bottom'] == $style['borders']['left']
&& $style['borders']['bottom'] == $style['borders']['right']
) {
$style['borders']['allBorders'] = $style['borders']['bottom'];
unset($style['borders']['bottom'], $style['borders']['top']);
unset($style['borders']['left'], $style['borders']['right']);
}
$this->sheet()->getStyle($rangeTo)->applyFromArray($style);
return $this;
}
/**
* Find a cell with the value
*
* @param mixed $value
* @param bool $strict
* @return array|null
*/
public function findCell($value, bool $strict = false): ?array
{
foreach ($this->getValues(null) as $row => $rowData) {
foreach ($rowData as $column => $columnData) {
if (($strict && $columnData === $value) || (! $strict && $columnData == $value)) {
return [$column, $row];
}
}
}
return null;
}
/**
* Duplicate rows (with style, value) by range
*
* @param string $ceilRange
* @param callable $value
* @param int $indentRows
* @param bool $addRows
* @return self
*/
public function duplicateRows(string $ceilRange, callable $value, int $indentRows = 0, bool $addRows = true): self
{
$range = explode(':', $ceilRange);
$range[0] = preg_split('#([A-Z]+)([\d]+)#S', $range[0], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$range[1] = preg_split('#([A-Z]+)([\d]+)#S', $range[1], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$shift = $range[1][1] - $range[0][1] + 1 + $indentRows;
$mergeCells = $this->getMergeCells();
$values = $this->getValues($ceilRange);
// Rows
if ($addRows) {
$this->addRow($range[1][1] + 1, $shift + $indentRows);
}
// Merge
foreach ($mergeCells as $item) {
$item = explode(':', $item);
$item[0] = preg_split('#([A-Z]+)([\d]+)#S', $item[0], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$item[1] = preg_split('#([A-Z]+)([\d]+)#S', $item[1], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
if (
$item[0][1] >= $range[0][1] && $item[0][1] <= $range[1][1] // rows
&& $item[1][1] >= $range[0][1] && $item[1][1] <= $range[1][1]
&& $this->isColumnGE($item[0][0], $range[0][0]) && $this->isColumnLE($item[0][0], $range[1][0]) // columns
&& $this->isColumnGE($item[1][0], $range[0][0]) && $this->isColumnLE($item[1][0], $range[1][0])
) {
$this->mergeCells($item[0][0].($item[0][1] + $shift) . ':' . $item[1][0].($item[1][1] + $shift));
}
}
$curr = $range[0][1];
while ($curr <= $range[1][1]) { // rows
// Height
$this->copyHeight($curr, $curr + $shift);
// Style, CellFormat, Value
$column = $range[0][0];
while ($this->isColumnLE($column, $range[1][0])) {
$this->copyStyle($column . $curr, $column . ($curr + $shift));
$this->copyCellFormat($column . $curr, $column . ($curr + $shift));
if (isset($values[$curr][$column])) {
$this->setValue($column . ($curr + $shift), $value($values[$curr][$column], $column, $curr), false);
}
$column = $this->strIncrement($column);
}
$curr++;
}
return $this;
}
/**
* Set custom style for the range of cells
*
* @param string $range
* @param array $style
* @return self
*/
public function setStyle(string $range, array $style): self
{
if (isset($style['bold'])) {
$this->sheet()->getStyle($range)->getFont()->setBold($style['bold']);
}
if (isset($style['italic'])) {
$this->sheet()->getStyle($range)->getFont()->setItalic($style['italic']);
}
if (isset($style['size'])) {
$this->sheet()->getStyle($range)->getFont()->setSize($style['size']);
}
if (isset($style['underline'])) {
$this->sheet()->getStyle($range)->getFont()->setUnderline($style['underline']);
}
if (isset($style['color'])) {
$this->sheet()->getStyle($range)->getFont()->getColor()->setRGB($style['color']);
}
if (isset($style['background_color'])) {
$this
->sheet()
->getStyle($range)
->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()
->setRGB($style['background_color']);
}
if (isset($style['borders'])) {
$this
->sheet()
->getStyle($range)
->getBorders()
->getAllBorders()
->setBorderStyle($style['borders'] ? Border::BORDER_THIN : Border::BORDER_NONE);
}
if (isset($style['borders_outline'])) {
$this
->sheet()
->getStyle($range)
->getBorders()
->getOutline()
->setBorderStyle($style['borders_outline'] ? Border::BORDER_THIN : Border::BORDER_NONE);
}
if (isset($style['align'])) {
$align = match ($style['align']) {
\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT => 'left',
\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER => 'center',
\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT => 'right',
};
$this
->sheet()
->getStyle($range)
->getAlignment()->setHorizontal($align);
}
if (isset($style['valign'])) {
$valign = match ($style['valign']) {
\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP => 'top',
\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER => 'center',
\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM => 'bottom',
};
$this
->sheet()
->getStyle($range)
->getAlignment()->setVertical($valign);
}
if (isset($style['wrap'])) {
$this
->sheet()
->getStyle($range)
->getAlignment()->setWrapText($style['wrap']);
}
return $this;
}
/**
* Place an image
*
* @param string $filename
* @param string $cell
* @param array $options
* @return self
*/
public function insertImage(string $filename, string $cell, array $options = []): self
{
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
if (isset($options['base64'])) {
$filename = 'data:image/' . $options['base64'] . ';base64,' . base64_encode(file_get_contents($filename));
}
$drawing->setPath($filename);
$drawing->setCoordinates($cell);
if (isset($options['coordinates2'])) {
$drawing->setCoordinates2($options['coordinates2']);
}
if (isset($options['name'])) {
$drawing->setName($options['name']);
}
if (isset($options['offset_x'])) {
$drawing->setOffsetX($options['offset_x']);
}
if (isset($options['offset_x2'])) {
$drawing->setOffsetX2($options['offset_x2']);
}
if (isset($options['offset_y'])) {
$drawing->setOffsetY($options['offset_y']);
}
if (isset($options['offset_y2'])) {
$drawing->setOffsetY2($options['offset_y2']);
}
if (isset($options['rotation'])) {
$drawing->setRotation($options['rotation']);
}
if (isset($options['width']) && isset($options['height'])) {
$drawing
->setResizeProportional(false)
->setWidth($options['width'])
->setHeight($options['height']);
} elseif (isset($options['width'])) {
$drawing->setWidth($options['width']);
} elseif (isset($options['height'])) {
$drawing->setHeight($options['height']);
}
$drawing->setWorksheet($this->sheet());
return $this;
}
/**
* "Reader" configuration
*
* @param \AnourValar\Office\Drivers\PhpSpreadsheetDriver $instance
* @return void
*/
protected function readConfiguration(PhpSpreadsheetDriver $instance): void
{
//
}
/**
* "Writer" configuration
*
* @param \PhpOffice\PhpSpreadsheet\Writer\IWriter $writer
* @return void
*/
protected function writeConfiguration(\PhpOffice\PhpSpreadsheet\Writer\IWriter $writer): void
{
if ($writer instanceof \PhpOffice\PhpSpreadsheet\Writer\Csv) {
$writer->setDelimiter(';')->setUseBOM(true);
}
}
/**
* @param \AnourValar\Office\Format $format
* @return string
*/
protected function getFormat(\AnourValar\Office\Format $format): string
{
return match ($format) {
\AnourValar\Office\Format::Xlsx => 'Xlsx',
\AnourValar\Office\Format::Pdf => 'Mpdf',
\AnourValar\Office\Format::Html => 'Html',
\AnourValar\Office\Format::Ods => 'Ods',
\AnourValar\Office\Format::Csv => 'Csv',
default => throw new \RuntimeException('Format is not supported.'),
};
}
}
================================================
FILE: src/Drivers/SaveInterface.php
================================================
load(__DIR__ . '/../resources/grid.xlsx', \AnourValar\Office\Format::Xlsx);
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\LoadInterface::load()
* @psalm-suppress InaccessibleProperty
*/
public function load(string $file, \AnourValar\Office\Format $format): self
{
if (! in_array($format, [\AnourValar\Office\Format::Docx, \AnourValar\Office\Format::Xlsx])) {
throw new \LogicException('Driver only supports Docx, Xlsx formats.');
}
$instance = new static();
$fileSystem = [];
$zipArchive = new \ZipArchive();
$zipArchive->open($file);
try {
$count = $zipArchive->numFiles;
for ($i = 0; $i < $count; $i++) {
$filename = $zipArchive->getNameIndex($i);
$content = $zipArchive->getFromName($filename);
$fileSystem[$filename] = $content;
}
} catch (\Throwable $e) {
$zipArchive->close();
throw $e;
}
$zipArchive->close();
$instance->fileSystem = $fileSystem;
$instance->format = $format;
return $instance;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\SaveInterface::save()
*/
public function save(string $file, \AnourValar\Office\Format $format): void
{
if ($format != $this->format) {
throw new \LogicException('Driver only supports saving in the same format.');
}
if (class_exists(\ZipStream\Option\Archive::class)) {
$zipStream = new \ZipStream\ZipStream(); // 2.x
} else {
$zipStream = new \ZipStream\ZipStream(sendHttpHeaders: false, defaultEnableZeroHeader: false); // 3.x
}
ob_start();
try {
foreach ($this->fileSystem as $filename => $content) {
$zipStream->addFile($filename, $content);
}
} catch (\Throwable $e) {
$zipStream->finish();
ob_get_clean();
throw $e;
}
$zipStream->finish();
file_put_contents($file, ob_get_clean());
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\DocumentInterface::replace()
*/
public function replace(array $data): self
{
foreach ($data as &$value) {
$value = $this->escape($value);
}
unset($value);
foreach ($this->fileSystem as $filename => &$content) {
if ($content && mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION)) == 'xml') {
$content = $this->handleReplace($content, $data);
}
}
unset($content);
return $this;
}
/**
* {@inheritDoc}
* @see \AnourValar\Office\Drivers\GridInterface::setGrid()
*/
public function setGrid(iterable $data): self
{
$sheet = ''; // matrix
$cols = ''; // columns
$buckets = []; // text (sharedStrings)
$bucketsIndex = 0;
$row = 0; // rows count
$columnsCount = 0; // columns count
$firstColumn = 'A'; // columns shift
// Styles
$styles = $this->loadGridStyles();
// Head (titles)
while ($data->valid()) {
$row++;
$titles = $data->current();
$data->next();
$columnsCount = count($titles);
if (! $titles) {
continue;
}
$sheet .= '';
$column = 'A';
foreach ($titles as $value) {
$value = (string) $value;
if ($value === '') {
$firstColumn = $this->strIncrement($firstColumn);
$column = $this->strIncrement($column);
continue;
}
$value = $this->escape($value);
$curr = $buckets[$value] ?? null;
if ($curr === null) {
$curr = $bucketsIndex;
$buckets[$value] = $curr;
$bucketsIndex++;
}
$sheet .= ''.$curr.'';
$column = $this->strIncrement($column);
}
$sheet .= '
';
break;
}
// Custom styles
foreach (($this->gridOptions['style'] ?? []) as $column => $alias) {
if (isset($styles[$alias])) {
$styles[$column] = $styles[$alias];
}
}
// Body (data)
while ($data->valid()) {
$values = $data->current();
$data->next();
$row++;
$ht = '';
if (isset($this->gridOptions['height'][$row])) {
$ht = 'ht="'.$this->gridOptions['height'][$row].'" customHeight="1" ';
}
$sheet .= '';
$column = 'A';
foreach ($values as $value) {
if ($value instanceof \Stringable && ! $value instanceof \DateTimeInterface) {
$value = (string) $value;
}
if ($value === null || $value === '') {
if ($this->isColumnGE($column, $firstColumn)) {
$sheet .= '';
}
} elseif (is_string($value)) {
$value = $this->escape($value);
$curr = $buckets[$value] ?? null;
if ($curr === null) {
$curr = $bucketsIndex;
$buckets[$value] = $curr;
$bucketsIndex++;
}
$style = ($styles[$column] ?? $styles['string']);
$sheet .= ''.$curr.'';
} elseif (is_double($value)) {
$style = ($styles[$column] ?? $styles['double']);
$sheet .= ''.$value.'';
} elseif (is_integer($value)) {
$style = ($styles[$column] ?? $styles['integer']);
$sheet .= ''.$value.'';
} elseif ($value instanceof \DateTimeInterface) {
$style = ($styles[$column] ?? $styles['date']);
$sheet .= ''.$this->excelDate($value).'';
} else {
throw new \RuntimeException('Unsupported type of value.');
}
$column = $this->strIncrement($column);
}
$sheet .= '
';
}
// Columns
$column = 'A';
for ($index = 1; $index <= $columnsCount; $index++) {
if ($this->isColumnGE($column, $firstColumn)) {
$width = ($this->gridOptions['width'][$column] ?? 20);
$cols .= '';
}
$column = $this->strIncrement($column);
}
// Save buckets
$this->saveGridSharedStrings($buckets);
// Save columns & matrix
$this->saveGridWorksheet($cols, $sheet, $row, $column);
// Etc
$this->saveGridEtc();
return $this;
}
/**
* @param string $content
* @param array $data
* @return string
*/
protected function handleReplace(string $content, array &$data): string
{
foreach ($data as $from => $to) {
$pattern = mb_str_split($from);
foreach ($pattern as &$patternItem) {
$patternItem = preg_quote($patternItem);
$patternItem .= '(\<[^\[]*)?';
}
unset($patternItem);
$pattern = implode('', $pattern);
$content = preg_replace_callback("#$pattern#Uu", function ($patterns) use ($from, $to) {
if (strip_tags($patterns[0]) == $from) {
return $to;
}
return $patterns[0];
}, $content);
}
return $content;
}
/**
* Set styles map for the grid template [header, integer, double, double_10, string, date, percentage, ...]
*
* @param string $column
* @param string $style
* @return self
*/
public function setStyle(string $column, string $style): self
{
$this->gridOptions['style'][$column] = $style;
return $this;
}
/**
* Set column's width for the grid
*
* @param string $column
* @param int $width
* @return self
*/
public function setWidth(string $column, int $width): self
{
$this->gridOptions['width'][$column] = $width;
return $this;
}
/**
* Set row's height for the grid
*
* @param string $row
* @param int|float $height
* @return self
*/
public function setHeight(string $row, int|float $height): self
{
$this->gridOptions['height'][$row] = $height;
return $this;
}
/**
* Set sheet title for the grid
*
* @param string $title
* @return self
*/
public function setSheetTitle(string $title): self
{
$this->fileSystem['xl/workbook.xml'] = preg_replace(
'#\#uU',
'',
$this->fileSystem['xl/workbook.xml']
);
return $this;
}
/**
* @return array
*/
protected function loadGridStyles(): array
{
$styles = [];
// Bucket
preg_match_all('#(.*)#uU', $this->fileSystem['xl/sharedStrings.xml'], $buckets);
$buckets = $buckets[1];
// Matrix
preg_match_all(
'#(\d+)#uU',
$this->fileSystem['xl/worksheets/sheet1.xml'],
$matrix
);
// Parse the map
foreach ($matrix[2] as $key => $value) {
if (isset($buckets[$value])) {
$styles[mb_substr($buckets[$value], 1, -1)] = $matrix[1][$key];
}
}
// Presets
return array_merge(['header' => 1, 'string' => 1, 'double' => 1, 'integer' => 1, 'date' => 1], $styles);
}
/**
* @param array $buckets
* @return void
*/
protected function saveGridSharedStrings(array &$buckets): void
{
$sst = '';
foreach (array_keys($buckets) as $word) {
$sst .= ''.$word.'';
}
$count = count($buckets);
$this->fileSystem['xl/sharedStrings.xml'] = <<
$sst
HERE;
}
/**
* @param string $cols
* @param string $sheet
* @param int $lastRow
* @param string $lastColumn
* @return void
*/
protected function saveGridWorksheet(string &$cols, string &$sheet, int $lastRow, string $lastColumn): void
{
$worksheet = 'xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" '
. 'xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" '
. 'xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" '
. 'mc:Ignorable="x14ac xr xr2 xr3" '
. 'xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" '
. 'xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" '
. 'xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" '
. 'xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" '
. 'xr:uid="{9CD2C5FF-272A-44F0-88FE-0A0D7B1A3B22}"';
if ($cols) {
$cols = "$cols";
}
if ($sheet) {
$sheet = "$sheet";
} else {
$sheet = '';
}
if (! $lastRow) {
$lastRow = 1;
}
$this->fileSystem['xl/worksheets/sheet1.xml'] = <<
$cols
$sheet
HERE;
}
/**
* @return void
*/
protected function saveGridEtc(): void
{
// Created at timestamp
$this->fileSystem['docProps/core.xml'] = preg_replace(
'#(\)(.*?)(\<\/dcterms\:created\>)#',
'${1}' . date('Y-m-d\TH:i:s\Z') . '$3',
$this->fileSystem['docProps/core.xml']
);
// Modified at timestamp
$this->fileSystem['docProps/core.xml'] = preg_replace(
'#(\)(.*?)(\<\/dcterms\:modified\>)#',
'${1}' . date('Y-m-d\TH:i:s\Z') . '$3',
$this->fileSystem['docProps/core.xml']
);
}
}
================================================
FILE: src/Facades/ExportGridInterface.php
================================================
buildBy($myGrid->query()->acl(), array_replace($this->profile, $this->profileExport)); // out of context
* $request = $this->getBuildRequest()->get();
*
* return response()->streamDownload(
* function () use ($generatorData, $myGrid, $exportService, $format, $request) {
* echo $exportService->grid($generatorData, $myGrid, $format, $request);
* },
* $myGrid->fileName($format->fileExtension(), $request),
* ['Access-Control-Expose-Headers' => 'Content-Disposition']
* );
*/
interface ExportGridQueryInterface extends ExportGridInterface
{
/**
* Laravel's Query builder (base query)
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function query(): \Illuminate\Database\Eloquent\Builder;
}
================================================
FILE: src/Facades/ExportService.php
================================================
getDriver($format)))
->hookHeader(function (GridInterface $driver, mixed $header, string|int $key, string $column, int $rowNumber) use (&$extras) {
if (isset($header['width'])) {
$driver->setWidth($column, $header['width']);
}
if (isset($header['height'])) {
$driver->setHeight($rowNumber, $header['height']);
}
$extras = array_merge($extras, $this->handleExtras($driver, $column, $header));
return $header['title'];
})
->hookRow(function (GridInterface $driver, mixed $row, string|int $key, int $rowNumber) use ($grid, $request) {
return $grid->item($row, $driver, $rowNumber, $request);
})
->hookAfter(function (
GridInterface $driver,
?string $headersRange,
?string $dataRange,
?string $totalRange,
array $columns
) use ($grid, $request, &$extras) {
$driver->setSheetTitle($grid->sheetTitle($request));
foreach ($extras as $extra) {
$extra();
}
})
->generate($grid->columns($request), $dataGenerator)
->save($format);
}
/**
* @param \AnourValar\Office\Drivers\GridInterface $driver
* @param string $column
* @param array $header
* @return array
* @throws \RuntimeException
*/
protected function handleExtras(GridInterface $driver, string $column, array $header): array
{
$extras = [];
// percentage
if (! empty($header[self::PERCENTAGE])) {
if ($driver instanceof \AnourValar\Office\Drivers\ZipDriver) {
$driver->setStyle($column, 'percentage');
} elseif ($driver instanceof \AnourValar\Office\Drivers\PhpSpreadsheetDriver) {
$extras[] = fn () => $driver->setCellFormat($column, \AnourValar\Office\Drivers\PhpSpreadsheetDriver::FORMAT_PERCENTAGE);
} else {
throw new \RuntimeException('The driver does not support the "percentage" feature.');
}
}
// double_10
if (! empty($header[self::DOUBLE_10])) {
if ($driver instanceof \AnourValar\Office\Drivers\ZipDriver) {
$driver->setStyle($column, 'double_10');
} elseif ($driver instanceof \AnourValar\Office\Drivers\PhpSpreadsheetDriver) {
$extras[] = fn () => $driver->setCellFormat($column, \AnourValar\Office\Drivers\PhpSpreadsheetDriver::FORMAT_DOUBLE_10);
} else {
throw new \RuntimeException('The driver does not support the "double_10" feature.');
}
}
// datetime
if (! empty($header[self::DATETIME])) {
if ($driver instanceof \AnourValar\Office\Drivers\ZipDriver) {
$driver->setStyle($column, 'datetime');
} elseif ($driver instanceof \AnourValar\Office\Drivers\PhpSpreadsheetDriver) {
$extras[] = fn () => $driver->setCellFormat($column, \AnourValar\Office\Drivers\PhpSpreadsheetDriver::FORMAT_DATETIME);
} else {
throw new \RuntimeException('The driver does not support the "datetime" feature.');
}
}
return $extras;
}
/**
* @param \AnourValar\Office\Format $format
* @return \AnourValar\Office\Drivers\GridInterface
*/
protected function getDriver(Format $format): GridInterface
{
if ($format == Format::Xlsx) {
return new \AnourValar\Office\Drivers\ZipDriver();
}
return new \AnourValar\Office\Drivers\PhpSpreadsheetDriver();
}
}
================================================
FILE: src/Format.php
================================================
reader + writer
case Pdf = 'pdf'; // sheets | grid => writer
case Html = 'html'; // sheets | grid => reader + writer
case Ods = 'ods'; // sheets | grid => reader + writer
case Csv = 'csv'; // sheets | grid => reader + writer
case Docx = 'docx'; // document => reader + writer
/**
* @return string
*/
public function fileExtension(): string
{
return match ($this) {
Format::Xlsx => 'xlsx',
Format::Pdf => 'pdf',
Format::Html => 'html',
Format::Ods => 'ods',
Format::Csv => 'csv',
Format::Docx => 'docx',
};
}
/**
* MIME
*
* @return string
*/
public function contentType(): string
{
return match ($this) {
Format::Xlsx => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
Format::Pdf => 'application/pdf',
Format::Html => 'text/html',
Format::Ods => 'application/vnd.oasis.opendocument.spreadsheet',
Format::Csv => 'text/csv',
Format::Docx => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
};
}
}
================================================
FILE: src/Generated.php
================================================
driver = $driver;
}
/**
* Save generated document to the buffer
*
* @param \AnourValar\Office\Format $format
* @return string
*/
public function save(Format $format): string
{
ob_start();
if ($this->hookSave) {
($this->hookSave)($this->driver, $format);
} else {
$this->driver->save('php://output', $format);
}
return ob_get_clean();
}
/**
* Save generated document to the file
*
* @param string $filename
* @param \AnourValar\Office\Format|null $format
* @return int|null
*/
public function saveAs(string $filename, ?Format $format = null): ?int
{
if (! $format) {
$format = Format::from(mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION)));
}
return file_put_contents($filename, $this->save($format));
}
/**
* Set hookSave
*
* @param \Closure|null $closure
* @return self
*/
public function hookSave(?\Closure $closure): self
{
$this->hookSave = $closure;
return $this;
}
}
================================================
FILE: src/GridService.php
================================================
driver = $driver;
}
/**
* Generate a document from the template (grid)
*
* @param array $headers
* @param iterable|\Closure $data
* @param string $leftTopCorner
* @return \AnourValar\Office\Generated
*/
public function generate(array $headers, iterable|\Closure $data, string $leftTopCorner = 'A1'): Generated
{
// Handle with data
if ($data instanceof \Closure) {
$data = $data();
}
// Create new document
if ($this->hookLoad) {
$driver = ($this->hookLoad)($this->driver);
if ($driver instanceof Generated) {
$driver = $driver->driver;
}
} else {
$driver = $this->driver->create();
}
// Hook: before
if ($this->hookBefore) {
($this->hookBefore)($driver, $headers, $data, $leftTopCorner);
}
// Set data
$driver->setGrid(
$this->getGenerator($driver, $headers, $data, $leftTopCorner, $headersRange, $dataRange, $totalRange, $columns)()
);
// Hook: after
if ($this->hookAfter) {
($this->hookAfter)($driver, $headersRange, $dataRange, $totalRange, $columns);
}
return new Generated($driver);
}
/**
* Set hookLoad
*
* @param ?\Closure $closure
* @return self
*/
public function hookLoad(?\Closure $closure): self
{
$this->hookLoad = $closure;
return $this;
}
/**
* Set hookBefore
*
* @param ?\Closure $closure
* @return self
*/
public function hookBefore(?\Closure $closure): self
{
$this->hookBefore = $closure;
return $this;
}
/**
* Set hookHeader
*
* @param ?\Closure $closure
* @return self
*/
public function hookHeader(?\Closure $closure): self
{
$this->hookHeader = $closure;
return $this;
}
/**
* Set hookRow
*
* @param ?\Closure $closure
* @return self
*/
public function hookRow(?\Closure $closure): self
{
$this->hookRow = $closure;
return $this;
}
/**
* Set hookAfter
*
* @param ?\Closure $closure
* @return self
*/
public function hookAfter(?\Closure $closure): self
{
$this->hookAfter = $closure;
return $this;
}
/**
* @param \AnourValar\Office\Drivers\GridInterface $driver
* @param array $headers
* @param iterable $data
* @param string $leftTopCorner
* @param mixed $headersRange
* @param mixed $dataRange
* @param mixed $totalRange
* @param mixed $columns
* @return \Closure
* @psalm-suppress UnusedForeachValue
*/
protected function getGenerator(
\AnourValar\Office\Drivers\GridInterface $driver,
array &$headers,
iterable &$data,
string $leftTopCorner,
&$headersRange = null,
&$dataRange = null,
&$totalRange = null,
&$columns = null
): \Closure {
return function () use ($driver, &$headers, &$data, $leftTopCorner, &$headersRange, &$dataRange, &$totalRange, &$columns) {
$ltc = preg_split('|([A-Z]+)|', $leftTopCorner, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
// left top corner: row
$headerRow = 1;
while ($ltc[1] > 1) {
$ltc[1]--;
$headerRow++;
yield [];
}
// left top corner: column
$firstColumn = 'A';
$indent = [];
while ($this->isColumnLE($firstColumn, $ltc[0]) && $firstColumn != $ltc[0]) {
$firstColumn = $this->strIncrement($firstColumn);
$indent[] = '';
}
// Handle with header
$lastColumn = $firstColumn;
$isFirst = true;
$hasHeaders = false;
foreach ($headers as $key => &$header) {
if ($isFirst) {
$isFirst = false;
} else {
$lastColumn = $this->strIncrement($lastColumn);
}
if ($this->hookHeader) {
// Hook: header
$header = ($this->hookHeader)($driver, $header, $key, $lastColumn, $headerRow);
}
if ($header) {
$hasHeaders = true;
}
}
unset($header);
// First iteration with headers
if ($hasHeaders) {
yield array_merge($indent, $headers);
} else {
$headerRow--;
}
// Iterations with data
$dataRow = $headerRow;
$isFirst = ! $hasHeaders;
foreach ($data as $key => $row) {
// Hook: row
if ($this->hookRow) {
$row = ($this->hookRow)($driver, $row, $key, $dataRow + 1);
}
if (is_null($row)) {
continue;
}
if ($isFirst) {
foreach ($row as $item) {
if ($isFirst) {
$isFirst = false;
} else {
$lastColumn = $this->strIncrement($lastColumn);
}
}
}
yield array_merge($indent, $row);
$dataRow++;
}
// Statistic
$headersRange = null;
if ($hasHeaders) {
$headersRange = sprintf('%s%d:%s%d', $firstColumn, $headerRow, $lastColumn, $headerRow);
}
$dataRange = null;
if ($dataRow != $headerRow) {
$dataRange = sprintf('%s%d:%s%d', $firstColumn, ($headerRow + 1), $lastColumn, $dataRow);
}
$totalRange = null;
if ($hasHeaders || $dataRow != $headerRow) {
$totalRange = sprintf(
'%s%d:%s%d',
$firstColumn,
($hasHeaders ? $headerRow : ($headerRow + 1)),
$lastColumn,
$dataRow
);
}
$columns = [];
if ($totalRange) {
$keys = array_keys($headers);
while ($this->isColumnLE($firstColumn, $lastColumn)) {
if (! $keys) {
$columns[] = $firstColumn;
} else {
$columns[array_shift($keys)] = $firstColumn;
}
$firstColumn = $this->strIncrement($firstColumn);
}
}
};
}
}
================================================
FILE: src/Mixer.php
================================================
driver;
if (! $referenceDriver instanceof \AnourValar\Office\Drivers\MixInterface) {
throw new \LogicException('Driver must implements MixInterface.');
}
$titles = [];
$count = $referenceDriver->getSheetCount();
for ($i = 0; $i < $count; $i++) {
$referenceDriver->setSheet($i);
$titles[] = $referenceDriver->getSheetTitle();
}
foreach ($generated as $driver) {
if (! $driver instanceof \AnourValar\Office\Generated) {
throw new \LogicException('Input data must be instanceof Generated');
}
$driver = $driver->driver;
if (! $driver instanceof $referenceDriver) {
throw new \LogicException('All drivers should be instances of the same implementation.');
}
$count = $driver->getSheetCount();
for ($i = 0; $i < $count; $i++) {
$driver->setSheet($i);
$driver->setSheetTitle($titles[] = $this->getTitle($driver->getSheetTitle(), $titles));
$referenceDriver->mergeDriver($driver);
}
}
return new Generated($referenceDriver);
}
/**
* @param string $title
* @param array $titles
* @return string
*/
protected function getTitle(string $title, array $titles): string
{
while (in_array($title, $titles, true)) {
$title = preg_replace_callback(
'#\((\d+)\)$#',
fn ($patterns) => '(' . ++$patterns[1] . ')',
$title,
-1,
$count
);
if (! $count) {
$title .= ' (1)';
}
}
return $title;
}
}
================================================
FILE: src/Sheets/Parser.php
================================================
toArray();
}
return $data;
}
/**
* Get schema for a document
*
* @param array $values
* @param array $data
* @param array $mergeCells
* @return \AnourValar\Office\Sheets\SchemaMapper
*/
public function schema(array $values, array $data, array $mergeCells): SchemaMapper
{
$schema = new SchemaMapper();
// Step 0: Parse input arguments to a canon format
$values = $this->parseValues($values, $lastColumn);
$data = $this->parseData($data);
$mergeCells = $this->parseMergeCells($mergeCells);
// Step 1: Short path -> full path
$this->canonizeMarkers($values, $data);
// Step 2: Calculate additional rows & columns, redundant data
$dataSchema = $this->calculateDataSchema($values, $data, $mergeCells, $schema, $lastColumn);
// Step 3: Shift formulas
$this->shiftFormulas($dataSchema, $schema, $mergeCells);
// Step 4: Replace markers with data
$this->replaceMarkers($dataSchema, $data, $schema);
return $schema;
}
/**
* @param array $values
* @param mixed $lastColumn
* @return array
*/
protected function parseValues(array $values, &$lastColumn): array
{
$lastColumn = 'A';
foreach ($values as &$columns) {
$currLastColumn = array_key_last($columns);
if ($this->isColumnLE($lastColumn, $currLastColumn)) {
$lastColumn = $currLastColumn;
}
$columns = array_filter($columns, fn ($item) => $item !== null && $item !== '');
}
unset($columns);
return $values;
}
/**
* @param array $data
* @return array
*/
protected function parseData(array &$data): array
{
$result = [];
foreach ($this->dot($data) as $key => $value) {
if (is_array($value) && ! $value) {
continue;
}
$result[$key] = $value;
}
return $result;
}
/**
* @param array $mergeCells
* @return array
*/
protected function parseMergeCells(array $mergeCells): array
{
foreach ($mergeCells as &$item) {
$item = explode(':', $item);
$item[0] = preg_split('#([A-Z]+)([\d]+)#S', $item[0], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$item[1] = preg_split('#([A-Z]+)([\d]+)#S', $item[1], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
}
unset($item);
return $mergeCells;
}
/**
* @param array $values
* @param array $data
* @return void
*/
protected function canonizeMarkers(array &$values, array &$data): void
{
foreach ($values as &$columns) {
foreach ($columns as &$value) {
if (! is_string($value)) {
continue;
}
$value = preg_replace_callback(
'#\[(\$?\!\s*|\$?\=\s*)?([a-z\d\.\_\*]+)\]#iS',
function ($patterns) use ($data) {
if (array_key_exists($patterns[2], $data)) {
return $patterns[0];
}
$result = null;
foreach (explode('.', $patterns[2]) as $pattern) {
$changed = true;
$prevResult = $result;
while ($changed) {
$changed = false;
if ($this->isShortPath($result ? ($result . '.' . $pattern) : $pattern, $data)) {
if ($result) {
$result .= '.';
}
$result .= $pattern;
$changed = true;
}
if ($this->isShortPath($result . '.0', $data)) {
$result .= '.0';
$changed = true;
}
}
if ($result === $prevResult && ($pattern != '*' || mb_substr($result, -2) != '.0')) {
return $patterns[0];
}
}
if ($result && array_key_exists($result, $data) && ! is_array($data[$result])) {
$result = preg_replace('#\.0(\.|$)#S', '.*$1', $result);
return sprintf('[%s%s]', $patterns[1], $result);
}
return $patterns[0];
},
$value
);
}
unset($value);
}
unset($columns);
}
/**
* @param array $values
* @param array $data
* @param array $mergeCells
* @param \AnourValar\Office\Sheets\SchemaMapper $schema
* @param string $lastColumn
* @return array
* @psalm-suppress UnusedForeachValue
*/
protected function calculateDataSchema(
array &$values,
array &$data,
array &$mergeCells,
SchemaMapper &$schema,
string $lastColumn
): array {
$dataSchema = [];
$shift = 0;
$step = 0;
$stepLeft = 0;
$stepOrigin = 0;
$stepRows = 0;
// fill in missing rows
$prevRow = 0;
foreach (array_keys($values) as $row) {
$diff = ($row - $prevRow);
while ($diff > 1) {
$values[$row - $diff + 1] = [];
$diff--;
}
$prevRow = $row;
}
ksort($values);
foreach ($values as $row => $columns) {
$maxMergeY = 0;
$additionRows = 0;
$additionColumns = 0;
$additionColumn = null;
foreach ($columns as $column => $value) {
foreach (array_keys($data) as $markerName) {
if (! $this->hasMarker($markerName, $value)) {
continue;
}
$qty = 0;
$pattern = $markerName;
while ($pattern = $this->increment($pattern, true)) {
if (! array_key_exists($pattern, $data)) {
break;
}
$qty++;
}
$additionRows = max($additionRows, $qty);
$qty = 0;
$pattern = $markerName;
while ($pattern = $this->increment($pattern, false)) {
if (! array_key_exists($pattern, $data)) {
break;
}
$qty++;
}
if ($qty) {
$additionColumns = max($additionColumns, $qty);
$additionColumn = $column;
}
}
if (is_string($value)) {
$columns[$column] = preg_replace('#\.\*(\.|\])#S', '.0$1', $value);
}
}
if (! $stepRows && $this->shouldBeDeleted($columns, $data)) {
$this->deleteRow($schema, $mergeCells, $row + $shift);
$shift--;
continue;
}
if ($stepRows) {
$additionRows = $stepRows;
}
$currAdditionRows = $additionRows;
if (! $additionRows) {
foreach ($columns as $column => $value) {
if (
! preg_match('#\[(\$?\!\s*|\$?\=\s*)?[a-z][a-z\d\_\.]+\]#iS', (string) $value)
&& ! preg_match('#^\=[A-Z][A-Z\.\d]#', (string) $value)
) {
unset($columns[$column]);
}
}
if (! $columns) {
continue;
}
}
$curr = $additionColumn;
$additionColumnValue = isset($curr) ? ($columns[$curr] ?? null) : null;
$mergeMapX = [];
foreach ($mergeCells as $item) {
if ($additionColumn.($row + $shift) == $item[0][0].$item[0][1] && $item[0][1] == $item[1][1]) {
while ($this->isColumnLE($item[0][0], $item[1][0]) && $item[0][0] != $item[1][0]) {
$item[0][0] = $this->strIncrement($item[0][0]);
$mergeMapX[] = $item[0][0];
}
}
}
foreach ($mergeMapX as $mergeItemX) {
$curr = $this->strIncrement($curr);
}
while ($additionColumns) {
$curr = $this->strIncrement($curr);
$additionColumns--;
$additionColumnValue = $this->increments($additionColumnValue, false);
$columns[$curr] = $additionColumnValue;
$schema->copyStyle($additionColumn.($row + $shift), $curr.($row + $shift));
$schema->copyWidth($additionColumn, $curr);
if ($mergeMapX) {
$originalCurr = $curr;
foreach ($mergeMapX as $mergeItemX) {
$curr = $this->strIncrement($curr);
$schema->copyWidth($mergeItemX, $curr);
}
$schema->mergeCells(sprintf('%s%s:%s%s', $originalCurr, ($row + $shift), $curr, ($row + $shift)));
$mergeCells[] = [ [$originalCurr, ($row + $shift)], [$curr, ($row + $shift)] ]; // fill in
}
}
$dataSchema[$row + $shift] = $columns;
$originalRow = ($row + $shift);
if ($additionRows) {
$firstColumn = 'A';
while ($this->isColumnLE($firstColumn, $lastColumn)) {
if (! isset($columns[$firstColumn]) && ! $this->insideMerge($firstColumn, $originalRow, $mergeCells)) {
$columns[$firstColumn] = null;
}
$firstColumn = $this->strIncrement($firstColumn);
}
uksort($columns, fn ($a, $b) => $this->isColumnLE($a, $b) ? -1 : 1);
foreach ($columns as $currKey => $currValue) {
$hasMarker = preg_match('#\[([a-z][a-z\d\.\_]+)\]#iS', (string) $currValue);
foreach ($mergeCells as $item) {
if ($currKey.$originalRow == $item[0][0].$item[0][1] && $item[0][1] != $item[1][1]) {
if (! $hasMarker) {
unset($columns[$currKey]);
continue;
}
$maxMergeY = max($maxMergeY, ($item[1][1] - $item[0][1]));
}
}
}
$shift += $maxMergeY;
}
while ($additionRows) {
$shift += $step;
$shift++;
$additionRows--;
foreach ($columns as &$column) {
if (is_string($column)) {
$column = $this->increments($column, true);
}
}
unset($column);
$dataSchema[$row + $shift] = array_filter($columns, fn ($item) => $item !== null && $item !== '');
if (! $step) {
$this->addRow($schema, $mergeCells, $row + $shift);
}
foreach (array_keys($columns) as $curr) {
$schema->copyStyle($curr.$originalRow, $curr.($row + $shift));
foreach ($mergeCells as $item) {
if ($curr.$originalRow == $item[0][0].$item[0][1]) {
$diff = $item[1][1] - $item[0][1];
$schema->mergeCells(
sprintf('%s%s:%s%s', $item[0][0], ($row + $shift), $item[1][0], ($row + $shift + $diff))
);
}
}
}
$iterate = $maxMergeY;
while ($iterate) {
$shift++;
$this->addRow($schema, $mergeCells, $row + $shift);
$iterate--;
}
}
if ($stepLeft) {
$stepLeft--;
}
if (! $stepLeft) {
$step = 0;
$stepRows = 0;
} else {
$stepOrigin--;
$shift -= $stepOrigin - $stepLeft;
}
if ($maxMergeY) {
$stepRows = $currAdditionRows;
$step = $maxMergeY;
$stepLeft = $step;
$stepOrigin = (($maxMergeY + 1) * ($currAdditionRows)) + $step;
$shift -= $stepOrigin;
}
}
unset($values);
return $dataSchema;
}
/**
* @param array $values
* @param \AnourValar\Office\Sheets\SchemaMapper $schema
* @param array $mergeCells
* @return void
*/
protected function shiftFormulas(array &$values, SchemaMapper &$schema, array &$mergeCells): void
{
// Prepares
$ranges = [];
$map = [];
foreach ($values as $row => $columns) {
foreach ($columns as $column => $value) {
if (preg_match('#^\=[A-Z][A-Z\.\d]#', (string) $value)) {
$map[$row][$column] = $value;
}
}
}
// "Outside" shifts
foreach ($schema->getOriginal()['rows'] as $action) {
foreach ($map as $row => $columns) {
foreach ($columns as $column => $value) {
$map[$row][$column] = $values[$row][$column] = preg_replace_callback(
'#([A-Z]+)([\d]+)#S',
function ($patterns) use ($action) {
if ($action['action'] == 'add') {
if ($patterns[2] >= $action['row']) {
return $patterns[1] . ++$patterns[2];
}
} else {
if ($patterns[2] > $action['row']) {
return $patterns[1] . --$patterns[2];
}
}
return $patterns[0];
},
$value
);
}
}
}
// "Inside" shifts
$prev = 0;
$prevAction = null;
foreach ($schema->getOriginal()['rows'] as $action) {
if ($prevAction && $prevAction['row'] + 1 == $action['row'] && $action['action'] == 'add' && $prevAction['action'] == 'add') {
$prev++;
} else {
if ($prevAction && $prevAction['action'] == 'add') {
$ranges[] = ['from' => ($prevAction['row'] - $prev - 1), 'to' => ($prevAction['row'])];
}
$prev = 0;
}
foreach ($map as $row => $columns) {
foreach ($columns as $column => $value) {
$map[$row][$column] = $values[$row][$column] = preg_replace_callback(
'#([A-Z]+)([\d]+)#S',
function ($patterns) use ($action, $row, $prev) {
if ($action['action'] == 'add') {
if ($action['row'] == $row && ($row - $prev) == ($patterns[2] + 1)) {
return $patterns[1] . (++$patterns[2] + $prev);
}
}
return $patterns[0];
},
$value
);
}
}
$prevAction = $action;
}
if ($prev || ($prevAction && $prevAction['action'] == 'add')) {
$ranges[] = ['from' => ($prevAction['row'] - $prev - 1), 'to' => ($prevAction['row'])];
}
// Dynamic table ranges
foreach ($ranges as $key => $value) {
foreach ($mergeCells as $merge) {
if (
$value['from'] >= $merge[0][1]
&& $value['from'] <= $merge[1][1]
&& $merge[0][1] != $merge[1][1]
&& preg_match('#\[([a-z][a-z\d\.\_]+)\]#iS', ($values[$merge[0][1]][$merge[0][0]] ?? ''))
) {
unset($ranges[$key]);
}
}
}
foreach ($map as $row => $columns) {
foreach ($columns as $column => $value) {
$map[$row][$column] = $values[$row][$column] = preg_replace_callback(
'#([A-Z]+)([\d]+)\:([A-Z]+)([\d]+)#S',
function ($patterns) use ($ranges) {
if ($patterns[2] == $patterns[4]) {
foreach ($ranges as $range) {
if ($patterns[2] == $range['from']) {
return sprintf('%s%d:%s%d', $patterns[1], $patterns[2], $patterns[3], $range['to']);
}
}
}
return $patterns[0];
},
$value
);
}
}
}
/**
* @param array $dataSchema
* @param array $data
* @param \AnourValar\Office\Sheets\SchemaMapper $schema
* @return void
*/
protected function replaceMarkers(array &$dataSchema, array &$data, SchemaMapper &$schema): void
{
$canonizeKeys = ['scalar' => [], 'closure' => []];
$canonizeValues = ['scalar' => [], 'closure' => []];
foreach ($data as $from => $to) {
if (! preg_match('#^[a-z][a-z\d\.\_]+$#iS', $from)) {
continue;
}
if (is_scalar($to) || is_null($to)) {
$canonizeKeys['scalar'][] = "[$from]";
$canonizeValues['scalar'][] = $to;
} else {
$canonizeKeys['closure'][] = "[$from]";
$canonizeValues['closure'][] = $to;
}
}
foreach ($dataSchema as $row => $columns) {
ksort($columns);
foreach ($columns as $column => $value) {
if (is_string($value) && $this->shouldBeDeleted([$value], $data, '$')) {
$value = null;
}
if (is_string($value) && mb_strlen($value)) {
$value = preg_replace('#\[(\$?\!\s*|\$?\=\s*)[a-z][a-z\d\_\.]+\]#iS', '', $value);
$value = trim($value);
if (($key = array_search($value, $canonizeKeys['scalar'])) !== false) { // type (cast) support
$value = $canonizeValues['scalar'][$key];
} elseif (($key = array_search($value, $canonizeKeys['closure'])) !== false) {
$value = $canonizeValues['closure'][$key];
} else {
$value = str_replace($canonizeKeys['scalar'], $canonizeValues['scalar'], $value);
}
}
if (is_string($value) && mb_strlen($value)) {
$value = preg_replace('#\[[a-z][a-z\d\_\.]+\]#iS', '', $value);
$value = trim($value);
}
if (is_string($value) && ! mb_strlen($value)) {
$value = null;
}
$schema->addData($row, $column, $value);
}
}
}
/**
* @param string $path
* @param array $markers
* @return bool
*/
private function isShortPath(string $path, array $markers): bool
{
if (array_key_exists($path, $markers)) {
return true;
}
if (! str_ends_with($path, '.')) {
$path .= '.';
}
foreach (array_keys($markers) as $marker) {
if (strpos($marker, $path) === 0) {
return true;
}
}
return false;
}
/**
* @param string $marker
* @param string $value
* @return bool
*/
private function hasMarker(string $marker, ?string $value): bool
{
$value = preg_replace('#\.\*(\.|$|)#S', '.0$1', (string) $value, -1, $count);
if (! $count) {
return false;
}
if (strpos((string) $value, "[$marker]") !== false) {
return true;
}
return false;
}
/**
* @param array $columns
* @param array $data
* @param string $prefix
* @return bool
*/
private function shouldBeDeleted(array $columns, array &$data, string $prefix = ''): bool
{
$prefix = preg_quote($prefix);
foreach ($columns as $column) {
if (is_null($column)) {
continue;
}
preg_match_all("#\[{$prefix}\=\s*([a-z\d\.\_]+)\]#i", $column, $patterns);
foreach (($patterns[1] ?? []) as $marker) {
if (! empty($data[$marker])) {
continue;
}
foreach ($data as $key => $value) {
if (strpos($key, $marker.'.') === 0 && ! empty($value)) {
continue 2;
}
}
return true;
}
preg_match_all("#\[{$prefix}\!\s*([a-z\d\.\_]+)\]#i", $column, $patterns);
foreach (($patterns[1] ?? []) as $marker) {
if (! empty($data[$marker])) {
return true;
}
foreach ($data as $key => $value) {
if (strpos($key, $marker.'.') === 0 && ! empty($value)) {
return true;
}
}
}
}
return false;
}
/**
* @param string $markerName
* @param bool $first
* @param int $shift
* @return string|null
*/
private function increment(string $markerName, bool $first, int $shift = 1): ?string
{
$markerName = explode('.', $markerName);
if (! $first) {
$markerName = array_reverse($markerName);
}
if (! $first) {
$qty = 0;
foreach ($markerName as $item) {
if (is_numeric($item)) {
$qty++;
}
}
if ($qty < 2) {
return null;
}
}
foreach ($markerName as &$item) {
if (is_numeric($item)) {
$item += $shift;
if (! $first) {
$markerName = array_reverse($markerName);
}
return implode('.', $markerName);
}
}
unset($item);
return null;
}
/**
* @param string $value
* @param bool $first
* @param int $shift
* @return string|null
*/
private function increments(string $value, bool $first, int $shift = 1): ?string
{
return preg_replace_callback(
'#\[(\$?\!\s*|\$?\=\s*)?([a-z][a-z\d\.\_]+)\]#iS',
function ($patterns) use ($first, $shift) {
$patterns[2] = $this->increment($patterns[2], $first, $shift);
if ($patterns[2]) {
return '[' . $patterns[1] . $patterns[2] . ']';
}
return $patterns[0];
},
$value
);
}
/**
* @param \AnourValar\Office\Sheets\SchemaMapper $schema
* @param array $mergeCells
* @param int $row
* @return void
*/
private function addRow(SchemaMapper &$schema, array &$mergeCells, int $row): void
{
$schema->addRow($row);
foreach ($mergeCells as &$mergeCell) {
if ($mergeCell[0][1] >= $row) {
$mergeCell[0][1]++;
}
if ($mergeCell[1][1] >= $row) {
$mergeCell[1][1]++;
}
}
unset($mergeCell);
}
/**
* @param \AnourValar\Office\Sheets\SchemaMapper $schema
* @param array $mergeCells
* @param int $row
* @return void
*/
private function deleteRow(SchemaMapper &$schema, array &$mergeCells, int $row): void
{
$schema->deleteRow($row);
foreach ($mergeCells as &$mergeCell) {
if ($mergeCell[0][1] >= $row) {
$mergeCell[0][1]--;
}
if ($mergeCell[1][1] >= $row) {
$mergeCell[1][1]--;
}
}
unset($mergeCell);
}
/**
* @param string $column
* @param int $row
* @param array $mergeCells
* @return bool
*/
private function insideMerge(string $column, int $row, array &$mergeCells): bool
{
foreach ($mergeCells as $item) {
if (
$this->isColumnLE($item[0][0], $column)
&& $this->isColumnGE($item[1][0], $column)
&& $item[0][1] <= $row
&& $item[1][1] >= $row
&& ($item[0][0] != $column || $item[0][1] != $row)
) {
return true;
}
}
return false;
}
}
================================================
FILE: src/Sheets/SchemaMapper.php
================================================
[], //[ 1 => ['A' => 'foo'], '2' => ['B' => 'bar'] ]
'rows' => [], //[ ['action' => 'add', 'row' => 1, 'qty' => 1], ['action' => 'delete', 'row' => 2, 'qty' => 1] ]
'copy_style' => [], //[ ['from' => 'A1', 'to' => 'A2'] ]
'merge_cells' => [], //[ 'A1:B1', 'C1:D1']
'copy_width' => [], //[ ['from' => 'B', 'to' => 'C'] ]
];
/**
* @return array
*/
public function toArray(): array
{
ksort($this->payload['data']);
$this->normalizeRows($this->payload['rows']);
$this->normalizeCells($this->payload['copy_style']);
sort($this->payload['copy_width']);
return $this->payload;
}
/**
* @return array
*/
public function getOriginal(): array
{
return $this->payload;
}
/**
* @param int $row
* @param string $column
* @param mixed $value
* @return self
*/
public function addData(int $row, string $column, mixed $value): self
{
$this->payload['data'][$row][$column] = $value;
return $this;
}
/**
* @param int $rowBefore
* @return self
*/
public function addRow(int $rowBefore): self
{
$this->payload['rows'][] = ['action' => 'add', 'row' => $rowBefore, 'qty' => 1];
return $this;
}
/**
* @param int $row
* @return self
*/
public function deleteRow(int $row): self
{
$this->payload['rows'][] = ['action' => 'delete', 'row' => $row, 'qty' => 1];
return $this;
}
/**
* @param string $from
* @param string $to
* @return self
*/
public function copyStyle(string $from, string $to): self
{
$this->payload['copy_style'][$from.$to] = ['from' => $from, 'to' => $to];
return $this;
}
/**
* @param string $ceilRange
* @return self
*/
public function mergeCells(string $ceilRange): self
{
$this->payload['merge_cells'][] = $ceilRange;
return $this;
}
/**
* @param string $from
* @param string $to
* @return self
*/
public function copyWidth(string $from, string $to): self
{
$this->payload['copy_width'][$from . $to] = ['from' => $from, 'to' => $to];
return $this;
}
/**
* @param array $rows
* @return void
*/
protected function normalizeRows(array &$rows): void
{
$optimizedRows = [];
$curr = [];
foreach ($rows as $item) {
if ($curr) {
if ($item['action'] == 'add' && $curr['action'] == $item['action'] && $item['row'] == ($curr['row'] + $curr['qty'])) {
$curr['qty']++;
} else {
$optimizedRows[] = ['action' => $curr['action'], 'row' => $curr['row'], 'qty' => $curr['qty']];
$curr = null;
}
}
if (! $curr) {
$curr = ['action' => $item['action'], 'row' => $item['row'], 'qty' => $item['qty']];
}
}
if ($curr) {
$optimizedRows[] = ['action' => $curr['action'], 'row' => $curr['row'], 'qty' => $curr['qty']];
}
$rows = $optimizedRows;
}
/**
* @param array $data
* @return void
*/
protected function normalizeCells(array &$data): void
{
ksort($data, SORT_NATURAL);
$data = array_values($data);
$optimizedData = [];
$curr = [];
foreach ($data as $item) {
$expect = preg_split('#([A-Z]+)([\d]+)#S', $item['to'], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$expect[1]++; // @phpstan-ignore-line
$expect = implode('', $expect);
if ($curr) {
if ($item['from'] == $curr['from'] && $item['to'] == $curr['expect']) {
$curr['append'] = ':' . $curr['expect'];
$curr['expect'] = $expect;
} else {
$optimizedData[] = ['from' => $curr['from'], 'to' => $curr['to'] . $curr['append']];
$curr = null;
}
}
if (! $curr) {
$curr = ['from' => $item['from'], 'to' => $item['to'], 'append' => '', 'expect' => $expect];
}
}
if ($curr) {
$optimizedData[] = ['from' => $curr['from'], 'to' => $curr['to'] . $curr['append']];
}
$data = $optimizedData;
}
}
================================================
FILE: src/SheetsService.php
================================================
driver = $driver;
$this->parser = $parser;
}
/**
* Generate a document from the template (sheets)
*
* @param string|\Stringable $templateFile
* @param mixed $data
* @param bool $autoCellFormat
* @return \AnourValar\Office\Generated
*/
public function generate(string|\Stringable $templateFile, mixed $data, bool $autoCellFormat = false): Generated
{
// Handle with input data
$data = $this->parser->canonizeData($data);
// Open the template
$templateFormat = Format::tryFrom(mb_strtolower(pathinfo($templateFile, PATHINFO_EXTENSION))) ?? Format::Xlsx;
if ($this->hookLoad) {
$driver = ($this->hookLoad)($this->driver, $templateFile, $templateFormat);
if ($driver instanceof Generated) {
$driver = $driver->driver;
}
} else {
$driver = $this->driver->load($templateFile, $templateFormat);
}
// Hook: before
if ($this->hookBefore) {
($this->hookBefore)($driver, $data);
}
// Handle sheets
$count = $driver->getSheetCount();
for ($sheetIndex = 0; $sheetIndex < $count; $sheetIndex++) {
$driver->setSheet($sheetIndex);
$this->handleSheet($driver, $data, $sheetIndex, $autoCellFormat);
}
// Hook: after
if ($this->hookAfter) {
($this->hookAfter)($driver);
}
// Return
return new Generated($driver);
}
/**
* Set hookLoad
*
* @param ?\Closure $closure
* @return self
*/
public function hookLoad(?\Closure $closure): self
{
$this->hookLoad = $closure;
return $this;
}
/**
* Set hookBefore
*
* @param ?\Closure $closure
* @return self
*/
public function hookBefore(?\Closure $closure): self
{
$this->hookBefore = $closure;
return $this;
}
/**
* Set hookValue
*
* @param ?\Closure $closure
* @return self
*/
public function hookValue(?\Closure $closure): self
{
$this->hookValue = $closure;
return $this;
}
/**
* Set hookAfter
*
* @param ?\Closure $closure
* @return self
*/
public function hookAfter(?\Closure $closure): self
{
$this->hookAfter = $closure;
return $this;
}
/**
* @param \AnourValar\Office\Drivers\SheetsInterface $driver
* @param array $data
* @param int $sheetIndex
* @throws \LogicException
* @return void
*/
protected function handleSheet(SheetsInterface &$driver, array &$data, int $sheetIndex, bool $autoCellFormat): void
{
// Get schema of the document
$schema = $this->parser->schema($driver->getValues(null), $data, $driver->getMergeCells())->toArray();
// Rows
foreach ($schema['rows'] as $row) {
if ($row['action'] == 'add') {
$driver->addRow($row['row'], $row['qty']);
} elseif ($row['action'] == 'delete') {
$driver->deleteRow($row['row'], $row['qty']);
} else {
throw new \LogicException('Incorrect usage.');
}
}
// Copy style & cell format
foreach ($schema['copy_style'] as $item) {
$driver->copyStyle($item['from'], $item['to']);
if (! $autoCellFormat) {
$driver->copyCellFormat($item['from'], $item['to']);
}
}
// Merge cells
foreach ($schema['merge_cells'] as $item) {
$driver->mergeCells($item);
}
// Copy width
foreach ($schema['copy_width'] as $item) {
$driver->copyWidth($item['from'], $item['to']);
}
// Data
$driver->setValues($this->handleData($schema['data'], $driver, $sheetIndex), $autoCellFormat);
}
/**
* @param array $data
* @param \AnourValar\Office\Drivers\SheetsInterface $driver
* @param int $sheetIndex
* @return array
*/
protected function handleData(array $data, SheetsInterface $driver, int $sheetIndex): array
{
foreach ($data as $row => &$columns) {
foreach ($columns as $column => &$value) {
if ($value instanceof \Closure) {
// Private Closure
$value = $value($driver, $column, $row);
if (is_null($value)) {
unset($data[$row][$column]);
}
} elseif ($this->hookValue) {
// Hook: value
$value = ($this->hookValue)($driver, $column, $row, $value, $sheetIndex);
if (is_null($value)) {
unset($data[$row][$column]);
}
}
}
unset($value);
}
unset($columns);
return $data;
}
}
================================================
FILE: src/Traits/Parser.php
================================================
$value) {
if (is_array($value)) {
$result = array_replace($result, $this->dot($value, $prefix.$key.'.'));
} else {
$result[$prefix.$key] = $value;
}
}
return $result;
}
/**
* @param string $compareColumn
* @param string $referenceColumn
* @return bool
*/
protected function isColumnLE(string $compareColumn, string $referenceColumn): bool
{
$compareLength = strlen($compareColumn);
$referenceLength = strlen($referenceColumn);
if ($compareLength < $referenceLength) {
return true;
}
if ($compareLength > $referenceLength) {
return false;
}
return $compareColumn <= $referenceColumn;
}
/**
* @param string $compareColumn
* @param string $referenceColumn
* @return bool
*/
protected function isColumnGE(string $compareColumn, string $referenceColumn): bool
{
$compareLength = strlen($compareColumn);
$referenceLength = strlen($referenceColumn);
if ($compareLength > $referenceLength) {
return true;
}
if ($compareLength < $referenceLength) {
return false;
}
return $compareColumn >= $referenceColumn;
}
/**
* Polyfill
*
* @param string $value
* @return string
*/
protected function strIncrement(string $value): string
{
if (PHP_VERSION_ID >= 80300) {
return str_increment($value);
}
$value++; // @phpstan-ignore-line
return $value;
}
}
================================================
FILE: src/Traits/XFormat.php
================================================
format('Y');
$month = (int) $date->format('m');
$day = (int) $date->format('d');
$hours = (int) $date->format('H');
$minutes = (int) $date->format('i');
$seconds = (int) $date->format('s');
$leapYear = true;
if ($year == 1900 && $month <= 2) {
$leapYear = false;
}
$baseDate = 2415020;
if ($month > 2) {
$month -= 3;
} else {
$month += 9;
--$year;
}
$century = (int) substr($year, 0, 2);
$decade = (int) substr($year, 2, 2);
$excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5);
$excelDate += $day + 1721119 - $baseDate + $leapYear;
$excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
return (float) $excelDate + $excelTime;
}
/**
* @param string|null $value
* @return string
*/
protected function escape(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8', true);
}
}
================================================
FILE: tests/GridServiceTest.php
================================================
getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:A1', $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame('A1:A1', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
['foo'],
[ ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:A1', $headersRange);
$this->assertSame('A2:A2', $dataRange);
$this->assertSame('A1:A2', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
['foo'],
[ ['111'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:A1', $headersRange);
$this->assertSame('A2:A3', $dataRange);
$this->assertSame('A1:A3', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
['foo'],
[ ['foo-1'], ['foo-2'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:A1', $headersRange);
$this->assertSame('A2:A4', $dataRange);
$this->assertSame('A1:A4', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
['foo'],
[ ['foo-1'], ['foo-2'], ['foo-3'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:B1', $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame('A1:B1', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
['foo', 'bar'],
[ ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:B1', $headersRange);
$this->assertSame('A2:B2', $dataRange);
$this->assertSame('A1:B2', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
['foo', 'bar'],
[ ['foo-1', 'bar-1'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:B1', $headersRange);
$this->assertSame('A2:B3', $dataRange);
$this->assertSame('A1:B3', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
['foo', 'bar'],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:B1', $headersRange);
$this->assertSame('A2:B4', $dataRange);
$this->assertSame('A1:B4', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
['foo', 'bar'],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'], ['foo-3', 'bar-3'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:C1', $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame('A1:C1', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:C1', $headersRange);
$this->assertSame('A2:C2', $dataRange);
$this->assertSame('A1:C2', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ['foo-1', 'bar-1', 'baz-1'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:C1', $headersRange);
$this->assertSame('A2:C3', $dataRange);
$this->assertSame('A1:C3', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('A1:C1', $headersRange);
$this->assertSame('A2:C4', $dataRange);
$this->assertSame('A1:C4', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'], ['foo-3', 'bar-3', 'baz-3'] ],
);
}
/**
* @return void
*/
public function test_generate_statistic_with_headers_with_shift()
{
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:C5', $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame('C5:C5', $totalRange);
$this->assertSame(['one' => 'C'], $columns);
})
->generate(
['one' => 'foo'],
[ ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:C5', $headersRange);
$this->assertSame('C6:C6', $dataRange);
$this->assertSame('C5:C6', $totalRange);
$this->assertSame(['C'], $columns);
})
->generate(
['foo'],
[ ['111'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:C5', $headersRange);
$this->assertSame('C6:C7', $dataRange);
$this->assertSame('C5:C7', $totalRange);
$this->assertSame(['C'], $columns);
})
->generate(
['foo'],
[ ['foo-1'], ['foo-2'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:C5', $headersRange);
$this->assertSame('C6:C8', $dataRange);
$this->assertSame('C5:C8', $totalRange);
$this->assertSame(['C'], $columns);
})
->generate(
['foo'],
[ ['foo-1'], ['foo-2'], ['foo-3'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:D5', $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame('C5:D5', $totalRange);
$this->assertSame(['C', 'D'], $columns);
})
->generate(
['foo', 'bar'],
[ ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:D5', $headersRange);
$this->assertSame('C6:D6', $dataRange);
$this->assertSame('C5:D6', $totalRange);
$this->assertSame(['C', 'D'], $columns);
})
->generate(
['foo', 'bar'],
[ ['foo-1', 'bar-1'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:D5', $headersRange);
$this->assertSame('C6:D7', $dataRange);
$this->assertSame('C5:D7', $totalRange);
$this->assertSame(['C', 'D'], $columns);
})
->generate(
['foo', 'bar'],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:D5', $headersRange);
$this->assertSame('C6:D8', $dataRange);
$this->assertSame('C5:D8', $totalRange);
$this->assertSame(['C', 'D'], $columns);
})
->generate(
['foo', 'bar'],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'], ['foo-3', 'bar-3'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:E5', $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame('C5:E5', $totalRange);
$this->assertSame(['C', 'D', 'E'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:E5', $headersRange);
$this->assertSame('C6:E6', $dataRange);
$this->assertSame('C5:E6', $totalRange);
$this->assertSame(['C', 'D', 'E'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ['foo-1', 'bar-1', 'baz-1'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:E5', $headersRange);
$this->assertSame('C6:E7', $dataRange);
$this->assertSame('C5:E7', $totalRange);
$this->assertSame(['C', 'D', 'E'], $columns);
})
->generate(
['foo', 'bar', 'baz'],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'] ],
'C5'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame('C5:E5', $headersRange);
$this->assertSame('C6:E8', $dataRange);
$this->assertSame('C5:E8', $totalRange);
$this->assertSame(['one' => 'C', 'two' => 'D', 'three' => 'E'], $columns);
})
->generate(
['one' => 'foo', 'two' => 'bar', 'three' => 'baz'],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'], ['foo-3', 'bar-3', 'baz-3'] ],
'C5'
);
}
/**
* @return void
*/
public function test_generate_statistic_without_headers()
{
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame(null, $totalRange);
$this->assertSame([], $columns);
})
->generate(
[],
[ ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:A1', $dataRange);
$this->assertSame('A1:A1', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
[],
[ ['111'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:A2', $dataRange);
$this->assertSame('A1:A2', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
[],
[ ['foo-1'], ['foo-2'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:A3', $dataRange);
$this->assertSame('A1:A3', $totalRange);
$this->assertSame(['A'], $columns);
})
->generate(
[],
[ ['foo-1'], ['foo-2'], ['foo-3'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:B1', $dataRange);
$this->assertSame('A1:B1', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:B2', $dataRange);
$this->assertSame('A1:B2', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:B3', $dataRange);
$this->assertSame('A1:B3', $totalRange);
$this->assertSame(['A', 'B'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'], ['foo-3', 'bar-3'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:C1', $dataRange);
$this->assertSame('A1:C1', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1', 'baz-1'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:C2', $dataRange);
$this->assertSame('A1:C2', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'] ],
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('A1:C3', $dataRange);
$this->assertSame('A1:C3', $totalRange);
$this->assertSame(['A', 'B', 'C'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'], ['foo-3', 'bar-3', 'baz-3'] ],
);
}
/**
* @return void
*/
public function test_generate_statistic_without_headers_with_shift()
{
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame(null, $dataRange);
$this->assertSame(null, $totalRange);
$this->assertSame([], $columns);
})
->generate(
[],
[ ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:D4', $dataRange);
$this->assertSame('D4:D4', $totalRange);
$this->assertSame(['D'], $columns);
})
->generate(
[],
[ ['111'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:D5', $dataRange);
$this->assertSame('D4:D5', $totalRange);
$this->assertSame(['D'], $columns);
})
->generate(
[],
[ ['foo-1'], ['foo-2'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:D6', $dataRange);
$this->assertSame('D4:D6', $totalRange);
$this->assertSame(['D'], $columns);
})
->generate(
[],
[ ['foo-1'], ['foo-2'], ['foo-3'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:E4', $dataRange);
$this->assertSame('D4:E4', $totalRange);
$this->assertSame(['D', 'E'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:E5', $dataRange);
$this->assertSame('D4:E5', $totalRange);
$this->assertSame(['D', 'E'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:E6', $dataRange);
$this->assertSame('D4:E6', $totalRange);
$this->assertSame(['D', 'E'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1'], ['foo-2', 'bar-2'], ['foo-3', 'bar-3'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:F4', $dataRange);
$this->assertSame('D4:F4', $totalRange);
$this->assertSame(['D', 'E', 'F'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1', 'baz-1'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:F5', $dataRange);
$this->assertSame('D4:F5', $totalRange);
$this->assertSame(['D', 'E', 'F'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'] ],
'D4'
);
(new \AnourValar\Office\GridService($this->getDriver()))
->hookAfter(function ($driver, ?string $headersRange, ?string $dataRange, ?string $totalRange, array $columns) {
$this->assertSame(null, $headersRange);
$this->assertSame('D4:F6', $dataRange);
$this->assertSame('D4:F6', $totalRange);
$this->assertSame(['D', 'E', 'F'], $columns);
})
->generate(
[],
[ ['foo-1', 'bar-1', 'baz-1'], ['foo-2', 'bar-2', 'baz-2'], ['foo-3', 'bar-3', 'baz-3'] ],
'D4'
);
}
/**
* @return \AnourValar\Office\Drivers\GridInterface
* @psalm-suppress UnusedForeachValue
*/
protected function getDriver(): \AnourValar\Office\Drivers\GridInterface
{
return new class () implements \AnourValar\Office\Drivers\GridInterface {
public function create(): self
{
return $this;
}
public function setGrid(iterable $data): self
{
foreach ($data as $item) {
}
return $this;
}
public function save(string $file, \AnourValar\Office\Format $format): void
{
}
};
}
}
================================================
FILE: tests/SheetsParserTest.php
================================================
service = new \AnourValar\Office\Sheets\Parser();
}
/**
* @return void
*/
public function test_collision_names()
{
$data = [
[
'values' => [
1 => [
'A' => '[title]',
],
2 => [
'A' => '[request.title]',
],
3 => [
'A' => '[response.body]',
],
4 => [
'A' => '[body]',
],
5 => [
'A' => '[products.title.title]',
],
],
'data' => [
'title' => 'foo',
'request' => [],
'response' => ['body' => 'bar'],
'body' => [],
'products' => [
'title' => [111],
],
],
],
[
'values' => [
1 => [
'A' => '[title]',
],
2 => [
'A' => '[request.title]',
],
3 => [
'A' => '[response.body]',
],
4 => [
'A' => '[body]',
],
5 => [
'A' => '[products.title.title]',
],
],
'data' => [
'title' => 'foo',
'request' => null,
'response' => ['body' => 'bar'],
'body' => null,
'products' => [
'title' => [111],
],
],
],
[
'values' => [
1 => [
'A' => '[title]',
],
2 => [
'A' => '[request.title]',
],
3 => [
'A' => '[response.body]',
],
4 => [
'A' => '[body]',
],
5 => [
'A' => '[products.title.title]',
],
],
'data' => [
'title' => 'foo',
'response' => ['body' => 'bar'],
'products' => [
'title' => [111],
],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'foo'],
2 => ['A' => null],
3 => ['A' => 'bar'],
4 => ['A' => null],
5 => ['A' => null],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_empty()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo] []',
'B' => '[]',
'C' => '[bar] []',
'D' => '[foo] [baz.]',
'E' => '[foo] [б]',
'F' => '[foo] [$]',
'G' => '[foo] [%]',
'H' => '[foo] [5]',
'K' => '[foo] [a]',
],
],
'data' => [
'' => 'NO',
'bar' => 'BAR',
'baz' => ['' => 'BAZ'],
'5' => 'NO',
'б' => 'NO',
'$' => 'NO',
'%' => 'NO',
'a' => 'NO',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '[]',
'C' => 'BAR []',
'D' => 'BAZ',
'E' => '[б]',
'F' => '[$]',
'G' => '[%]',
'H' => '[5]',
'K' => '[a]',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_scalar()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => 'test [foo] test',
'E' => '[test10]',
'F' => '[test]',
'G' => '[test1]',
'H' => 'hello [$=foo]',
'I' => '[$= foo] world',
'J' => '[$! foo] test',
'K' => '[$! foo] test',
'L' => '[$= foo1] test',
'Q' => '=A1+B2+C3+D4+E5',
'X' => '= A1',
'W' => '=1',
'Y' => '=AB100 + [test]',
],
2 => [
'A' => '[bar]',
'B' => 'test [bar] test',
'C' => 'bar',
'D' => '[a.b] -> [a.c] -> [a.d] -> [a.e.f]',
'H' => '[hello]',
'J' => '[world]',
'K' => '[k] [9] [9k] [k9]',
'Y' => null,
],
'3' => [
'D' => '[bar] [!bar]',
'Y' => null,
],
'4' => [
'D' => 'hello world',
'Y' => null,
],
'5' => [
'A' => 1,
'B' => 2,
'Y' => null,
],
'6' => [
'Q' => '=A1+B2+C3+D4+E5',
'Y' => null,
],
],
'data' => [
'foo' => 'hello',
'bar' => 'world',
'a' => ['b' => '11', 'c' => '22', 'e' => ['f' => 'oops']],
'test10' => 12.5,
'test' => '3',
'test1' => 5,
'hello' => null,
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'hello',
'C' => 'test hello test',
'E' => 12.5,
'F' => '3',
'G' => 5,
'H' => 'hello',
'I' => 'world',
'J' => null,
'K' => null,
'L' => null,
'Q' => '=A1+B2+C3+D3+E4',
'Y' => '=AB99 + 3',
],
2 => [
'A' => 'world',
'B' => 'test world test',
'D' => '11 -> 22 -> -> oops',
'H' => null,
'J' => null,
'K' => '[k] [9] [9k]',
],
5 => [
'Q' => '=A1+B2+C3+D3+E4',
],
],
'rows' => [['action' => 'delete', 'row' => 3, 'qty' => 1]],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_not_scalar()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
'B' => '[baz]',
],
2 => [
'A' => 'hello [bar] world',
'B' => null,
],
3 => [
'A' => '[test] [=foo]',
'B' => null,
],
4 => [
'A' => '[test2] [=bar]',
'B' => null,
],
5 => [
'A' => '[test2] [!foo]',
'B' => null,
],
],
'data' => [
'foo' => function () {
},
'baz' => new \DateTime('2022-11-16'),
'test' => function () {
},
'test2' => function () {
throw new \LogicException('oops');
},
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => $item['data']['foo'],
'B' => $item['data']['baz'],
],
2 => [
'A' => 'hello world',
],
3 => [
'A' => $item['data']['test'],
],
],
'rows' => [
['action' => 'delete', 'row' => 4, 'qty' => 1],
['action' => 'delete', 'row' => 4, 'qty' => 1],
],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
$this->assertSame(
[
'data' => [
1 => [
'A' => 'hello',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema([1 => ['A' => 'hello [world]']], ['world' => function () {
}], [])->toArray()
);
}
/**
* @return void
*/
public function test_schema_conditions1()
{
$data = [
[
'values' => [
1 => [
'A' => 'AA [$= foo] [$= bar]',
'B' => 'BB [$= baz] [$= foo] [$= bar]',
'C' => 'CC [$= foo] [$= baz] [$= bar]',
'D' => 'DD [$= foo] [$= bar] [$= baz]',
],
2 => [
'A' => 'AA [$! baz] [$! foobar]',
'B' => 'BB [$! foo] [$! baz] [$! foobar]',
'C' => 'CC [$! baz] [$! foo] [$! foobar]',
'D' => 'DD [$! baz] [$! foobar] [$! foo]',
],
],
'data' => [
'foo' => 'foo',
'bar' => 'bar',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'AA',
'B' => null,
'C' => null,
'D' => null,
],
2 => [
'A' => 'AA',
'B' => null,
'C' => null,
'D' => null,
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_conditions2()
{
$data = [
[
'values' => [
1 => [
'A' => '11 [= foo] [= bar]',
],
2 => [
'A' => '22 [= baz] [= foo] [= bar]',
],
3 => [
'A' => '33 [= foo] [= baz] [= bar]',
],
4 => [
'A' => '44 [= foo] [= bar] [= baz]',
],
5 => [
'A' => '55 [! baz] [! foobar]',
],
6 => [
'A' => '66 [! foo] [! baz] [! foobar]',
],
7 => [
'A' => '77 [! baz] [! foo] [! foobar]',
],
8 => [
'A' => '88 [! baz] [! foobar] [! foo]',
],
],
'data' => [
'foo' => 'foo',
'bar' => 'bar',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '11',
],
2 => [
'A' => '55',
],
],
'rows' => [
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'delete', 'row' => 3, 'qty' => 1],
['action' => 'delete', 'row' => 3, 'qty' => 1],
['action' => 'delete', 'row' => 3, 'qty' => 1],
],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_zero_empty()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo] [=test]',
'C' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo] [= list]',
'C' => null,
],
3 => [
'A' => 'bar [= bar]',
'B' => '[bar] 111',
'C' => null,
],
4 => [
'A' => 'foo [= list.0]',
'B' => '[foo]',
'C' => null,
],
5 => [
'A' => 'bar',
'B' => '[bar] 222 [=bar]',
'C' => '=A3+B5+C7',
],
6 => [
'A' => 'foo [= list.0.c]',
'B' => '[foo] [!list]',
'C' => null,
],
7 => [
'A' => 'bar',
'B' => '[bar] 333 [! list]',
'C' => null,
],
],
'data' => [
'list' => [],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'bar',
'B' => 'test2 111',
],
2 => [
'B' => 'test2 222',
'C' => '=A1+B2+C3',
],
3 => [
'B' => 'test2 333',
],
],
'rows' => [
['action' => 'delete', 'row' => 1, 'qty' => 1],
['action' => 'delete', 'row' => 1, 'qty' => 1],
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'delete', 'row' => 3, 'qty' => 1],
],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"#$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_zero()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo [= list.0.c]',
'B' => '[foo]',
'K' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.0.c]',
'D' => '[list.0.d]',
'E' => '[foo] hello [list.0.c] -> [list.0.d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'I' => '=A1*B2*C3',
'K' => '=A2:A2',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A1*B2*C3',
'K' => null,
],
],
'data' => [
'list' => [],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo] [=list_c.0]',
'K' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo] [!list_c]',
'C' => '[list_c.0]',
'D' => '[list_d.0]',
'E' => '[foo] hello [list_c.0] -> [list_d.0] world [bar]',
'F' => 'bar',
'G' => '[bar] [! list_c]',
'I' => '=A1*B2*C3',
'K' => '=A2:A2',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A1*B2*C3',
'K' => null,
],
],
'data' => [
'list_c' => [],
'list_d' => [],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => null,
'D' => null,
'E' => 'test1 hello -> world test2',
'G' => 'test2',
'I' => '=A1*B1*C2',
'K' => '=A1:A1',
],
'2' => [
'B' => 'test2',
'I' => '=A1*B1*C2',
],
],
'rows' => [
['action' => 'delete', 'row' => 1, 'qty' => 1],
],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"#$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_one()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.0.c]',
'D' => '[list.0.d]',
'E' => '[foo] hello [list.0.c] -> [list.0.d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
4 => [
'A' => '[bar] [! list]',
'K' => null,
],
],
'data' => [
'list' => [ ['c' => '11', 'd' => '12'] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.c]',
'D' => '[list.d]',
'E' => '[foo] hello [list.c] -> [list.d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
4 => [
'A' => '[bar] [!list]',
'K' => null,
],
],
'data' => [
'list' => [ ['c' => '11', 'd' => '12'] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c.0]',
'D' => '[list_d.0]',
'E' => '[foo] hello [list_c.0] -> [list_d.0] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
4 => [
'A' => '[bar] [! list_c]',
'K' => null,
],
],
'data' => [
'list_c' => ['11'],
'list_d' => ['12'],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c]',
'D' => '[list_d]',
'E' => '[foo] hello [list_c] -> [list_d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
4 => [
'A' => '[bar] [!list_c]',
'K' => null,
],
],
'data' => [
'list_c' => ['11'],
'list_d' => ['12'],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'H' => '=A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'B' => 'test1',
'C' => '11',
'D' => '12',
'E' => 'test1 hello 11 -> 12 world test2',
'G' => 'test2',
'H' => '=A1*B2*C3',
],
3 => [
'B' => 'test2',
'H' => '=A1*B2*C3',
'K' => '=A2:A2',
],
],
'rows' => [['action' => 'delete', 'row' => 4, 'qty' => 1]],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_two()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.c]',
'D' => '[list.d]',
'E' => '[foo] hello [list.c] -> [list.d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
],
'data' => [
'list' => [ ['c' => '11', 'd' => '12'], ['c' => '21', 'd' => '22'] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.c]',
'D' => '[list.d]',
'E' => '[foo] hello [list.*.c] -> [list.*.d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
],
'data' => [
'list' => [ ['c' => '11', 'd' => '12'], ['c' => '21', 'd' => '22'] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c]',
'D' => '[list_d]',
'E' => '[foo] hello [list_c] -> [list_d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
],
'data' => [
'list_c' => ['11', '21'],
'list_d' => ['12', '22'],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c.*]',
'D' => '[list_d.*]',
'E' => '[foo] hello [list_c.*] -> [list_d.*] world [bar]',
'F' => 'bar',
'G' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'H' => '=A1*B2*C3',
'J' => 'A1*B2*C3',
'K' => '=A2:A2',
],
],
'data' => [
'list_c' => ['11', '21'],
'list_d' => ['12', '22'],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'H' => '=A1*B2*C4',
'K' => '=A2:A3',
],
2 => [
'A' => 'foo',
'B' => 'test1',
'C' => '11',
'D' => '12',
'E' => 'test1 hello 11 -> 12 world test2',
'F' => 'bar',
'G' => 'test2',
'H' => '=A1*B2*C4',
'J' => 'A1*B2*C3',
],
3 => [
'A' => 'foo',
'B' => 'test1',
'C' => '21',
'D' => '22',
'E' => 'test1 hello 21 -> 22 world test2',
'F' => 'bar',
'G' => 'test2',
'H' => '=A1*B3*C4',
'J' => 'A1*B2*C3',
],
4 => [
'B' => 'test2',
'H' => '=A1*B2*C4',
'K' => '=A2:A3',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 1],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3'],
['from' => 'B2', 'to' => 'B3'],
['from' => 'C2', 'to' => 'C3'],
['from' => 'D2', 'to' => 'D3'],
['from' => 'E2', 'to' => 'E3'],
['from' => 'F2', 'to' => 'F3'],
['from' => 'G2', 'to' => 'G3'],
['from' => 'H2', 'to' => 'H3'],
['from' => 'I2', 'to' => 'I3'],
['from' => 'J2', 'to' => 'J3'],
['from' => 'K2', 'to' => 'K3'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_three()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.c]',
'D' => '[list.d]',
'E' => '[foo] hello [list.c] -> [list.d] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list' => [ ['c' => 11, 'd' => 12.5], ['c' => '21', 'd' => '22'], ['c' => '31', 'd' => '32'] ],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.*.c]',
'D' => '[list.*.d]',
'E' => '[foo] hello [list.*.c] -> [list.*.d] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list' => [ ['c' => 11, 'd' => 12.5], ['c' => '21', 'd' => '22'], ['c' => '31', 'd' => '32'] ],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c]',
'D' => '[list_d]',
'E' => '[foo] hello [list_c] -> [list_d] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list_c' => [11, '21', '31'],
'list_d' => [12.5, '22', '32'],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c.*]',
'D' => '[list_d.*]',
'E' => '[foo] hello [list_c.*] -> [list_d.*] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list_c' => [11, '21', '31'],
'list_d' => [12.5, '22', '32'],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'I' => '=A2*C2+B1+D5+E$2',
'K' => '=A2:A4',
],
2 => [
'A' => 'foo',
'B' => 'test1',
'C' => 11,
'D' => 12.5,
'E' => 'test1 hello 11 -> 12.5 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A2*C2+B1+D5+E$2',
],
3 => [
'A' => 'foo',
'B' => 'test1',
'C' => '21',
'D' => '22',
'E' => 'test1 hello 21 -> 22 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A3*C3+B1+D5+E$2',
],
4 => [
'A' => 'foo',
'B' => 'test1',
'C' => '31',
'D' => '32',
'E' => 'test1 hello 31 -> 32 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A4*C4+B1+D5+E$2',
],
5 => [
'B' => 'test2',
'I' => '=A2*C2+B1+D5+E$2',
'K' => '=A2:A4',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'B2', 'to' => 'B3:B4'],
['from' => 'C2', 'to' => 'C3:C4'],
['from' => 'D2', 'to' => 'D3:D4'],
['from' => 'E2', 'to' => 'E3:E4'],
['from' => 'G2', 'to' => 'G3:G4'],
['from' => 'H2', 'to' => 'H3:H4'],
['from' => 'I2', 'to' => 'I3:I4'],
['from' => 'J2', 'to' => 'J3:J4'],
['from' => 'K2', 'to' => 'K3:K4'],
],
'merge_cells' => ['E3:F3', 'E4:F4'],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_three_limit()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'G' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.0.c]',
'D' => '[list.0.d]',
'E' => '[foo] hello [list.0.c] -> [list.0.d] world [bar]',
'F' => 'bar',
'G' => '[bar]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'G' => null,
],
],
'data' => [
'list' => [ ['c' => 11, 'd' => 12.5], ['c' => '21', 'd' => '22'], ['c' => '31', 'd' => '32'] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'G' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c.0]',
'D' => '[list_d.0]',
'E' => '[foo] hello [list_c.0] -> [list_d.0] world [bar]',
'F' => 'bar',
'G' => '[bar]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'G' => null,
],
],
'data' => [
'list_c' => [11, '21', '31'],
'list_d' => [12.5, '22', '32'],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
],
2 => [
'B' => 'test1',
'C' => 11,
'D' => 12.5,
'E' => 'test1 hello 11 -> 12.5 world test2',
'G' => 'test2',
],
3 => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_four()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.c]',
'D' => '[list.d]',
'E' => '[foo] hello [list.c] -> [list.d] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list' => [ ['c' => 11, 'd' => 12.5], ['c' => '21', 'd' => '22'], ['c' => '31', 'd' => '32'], ['c' => '41', 'd' => '42'] ],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list.*.c]',
'D' => '[list.*.d]',
'E' => '[foo] hello [list.*.c] -> [list.*.d] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list' => [ ['c' => 11, 'd' => 12.5], ['c' => '21', 'd' => '22'], ['c' => '31', 'd' => '32'], ['c' => '41', 'd' => '42'] ],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c]',
'D' => '[list_d]',
'E' => '[foo] hello [list_c] -> [list_d] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list_c' => [11, '21', '31', '41'],
'list_d' => [12.5, '22', '32', '42'],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[list_c.*]',
'D' => '[list_d.*]',
'E' => '[foo] hello [list_c.*] -> [list_d.*] world [bar]',
'G' => 'bar',
'H' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'I' => '=A2*C2+B1+D3+E$2',
'K' => '=A2:A2',
],
],
'data' => [
'list_c' => [11, '21', '31', '41'],
'list_d' => [12.5, '22', '32', '42'],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['E2:F2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'I' => '=A2*C2+B1+D6+E$2',
'K' => '=A2:A5',
],
2 => [
'A' => 'foo',
'B' => 'test1',
'C' => 11,
'D' => 12.5,
'E' => 'test1 hello 11 -> 12.5 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A2*C2+B1+D6+E$2',
],
3 => [
'A' => 'foo',
'B' => 'test1',
'C' => '21',
'D' => '22',
'E' => 'test1 hello 21 -> 22 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A3*C3+B1+D6+E$2',
],
4 => [
'A' => 'foo',
'B' => 'test1',
'C' => '31',
'D' => '32',
'E' => 'test1 hello 31 -> 32 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A4*C4+B1+D6+E$2',
],
5 => [
'A' => 'foo',
'B' => 'test1',
'C' => '41',
'D' => '42',
'E' => 'test1 hello 41 -> 42 world test2',
'G' => 'bar',
'H' => 'test2',
'I' => '=A5*C5+B1+D6+E$2',
],
6 => [
'B' => 'test2',
'I' => '=A2*C2+B1+D6+E$2',
'K' => '=A2:A5',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 3],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A5'],
['from' => 'B2', 'to' => 'B3:B5'],
['from' => 'C2', 'to' => 'C3:C5'],
['from' => 'D2', 'to' => 'D3:D5'],
['from' => 'E2', 'to' => 'E3:E5'],
['from' => 'G2', 'to' => 'G3:G5'],
['from' => 'H2', 'to' => 'H3:H5'],
['from' => 'I2', 'to' => 'I3:I5'],
['from' => 'J2', 'to' => 'J3:J5'],
['from' => 'K2', 'to' => 'K3:K5'],
],
'merge_cells' => ['E3:F3', 'E4:F4', 'E5:F5'],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_zero_empty()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0.c] [= test]',
],
3 => [
'A' => 'bar [= test]',
'B' => '[foo]',
'C' => null,
],
4 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => []] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo [= test]',
'B' => '[foo]',
'C' => '[matrix.0]',
],
3 => [
'A' => 'bar',
'B' => '[foo] [= test]',
'C' => null,
],
4 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [[]] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => '=SUM(A2:A2)',
],
2 => [
'B' => 'test2',
],
],
'rows' => [
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'delete', 'row' => 2, 'qty' => 1],
],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"#$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_zero()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => []] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => []] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [[]] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [[]] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => '=SUM(A2:A2)',
],
2 => [
'B' => 'test1',
'C' => null,
],
'3' => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"#$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_one()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0.c.0]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.c.*]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0.0.0]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.*.*]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [['11']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => '=SUM(A2:A2)',
],
2 => [
'B' => 'test1',
'C' => '11',
],
3 => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_two()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.c.*]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [['11', '12']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => '=SUM(A2:A2)',
],
2 => [
'B' => 'test1',
'C' => '11',
'D' => '12',
],
3 => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [
['from' => 'C2', 'to' => 'D2'],
],
'merge_cells' => [],
'copy_width' => [['from' => 'C', 'to' => 'D']],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_two_limit()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0.c.0]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => null,
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.0.0.0]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [['11', '12']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
],
2 => [
'B' => 'test1',
'C' => '11',
],
3 => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_three()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12', '13']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.c]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12', '13']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix.*.c.*]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ ['c' => ['11', '12', '13']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
],
2 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '[matrix]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => null,
],
],
'data' => [
'matrix' => [ [['11', '12', '13']] ],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => '=SUM(A2:A2)',
],
2 => [
'B' => 'test1',
'C' => '11',
'D' => '12',
'E' => '13',
],
3 => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [
['from' => 'C2', 'to' => 'D2'],
['from' => 'C2', 'to' => 'E2'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'C', 'to' => 'D'],
['from' => 'C', 'to' => 'E'],
],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_equal()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
'D' => null,
],
2 => [
'A' => 'foo',
'B' => '[matrix.b]',
'C' => '[matrix.c]',
'D' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => 'foo: [foo] bar: [bar]',
'D' => 'bar: [bar] foo: [foo]',
],
],
'data' => [
'matrix' => [
['b' => 'b1', 'c' => ['c1', 'd1', 'e1']],
['b' => 'b2', 'c' => ['c2', 'd2', 'e2']],
['b' => 'b3', 'c' => ['c3', 'd3', 'e3']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
'D' => null,
],
2 => [
'A' => 'foo',
'B' => '[matrix.*.b]',
'C' => '[matrix.*.c]',
'D' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => 'foo: [foo] bar: [bar]',
'D' => 'bar: [bar] foo: [foo]',
],
],
'data' => [
'matrix' => [
['b' => 'b1', 'c' => ['c1', 'd1', 'e1']],
['b' => 'b2', 'c' => ['c2', 'd2', 'e2']],
['b' => 'b3', 'c' => ['c3', 'd3', 'e3']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'C' => '=SUM(A2:A2)',
'D' => null,
],
2 => [
'A' => 'foo',
'B' => '[matrix.*.b]',
'C' => '[matrix.*.c]',
'D' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => 'foo: [foo] bar: [bar]',
'D' => 'bar: [bar] foo: [foo]',
],
],
'data' => [
'matrix' => [
['b' => 'b1', 'c' => ['c1', 'd1', 'e1']],
['b' => 'b2', 'c' => ['c2', 'd2', 'e2']],
['b' => 'b3', 'c' => ['c3', 'd3', 'e3']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'C' => '=SUM(A2:A4)',
],
2 => [
'A' => 'foo',
'B' => 'b1',
'C' => 'c1',
'D' => 'd1',
'E' => 'e1',
],
3 => [
'A' => 'foo',
'B' => 'b2',
'C' => 'c2',
'D' => 'd2',
'E' => 'e2',
],
4 => [
'A' => 'foo',
'B' => 'b3',
'C' => 'c3',
'D' => 'd3',
'E' => 'e3',
],
5 => [
'B' => 'test2',
'C' => 'foo: test1 bar: test2',
'D' => 'bar: test2 foo: test1',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'B2', 'to' => 'B3:B4'],
['from' => 'C2', 'to' => 'C3:C4'],
['from' => 'C2', 'to' => 'D2'],
['from' => 'C2', 'to' => 'E2'],
['from' => 'D2', 'to' => 'D3:D4'],
['from' => 'E2', 'to' => 'E3:E4'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'C', 'to' => 'D'],
['from' => 'C', 'to' => 'E'],
],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_equal_limit()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'D' => null,
],
2 => [
'A' => 'foo',
'B' => '[matrix.0.b]',
'C' => '[matrix.0.c.0]',
'D' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => 'foo: [foo] bar: [bar]',
'D' => 'bar: [bar] foo: [foo]',
],
],
'data' => [
'matrix' => [
['b' => 'b1', 'c' => ['c1', 'd1', 'e1']],
['b' => 'b2', 'c' => ['c2', 'd2', 'e2']],
['b' => 'b3', 'c' => ['c3', 'd3', 'e3']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
],
2 => [
'B' => 'b1',
'C' => 'c1',
],
3 => [
'B' => 'test2',
'C' => 'foo: test1 bar: test2',
'D' => 'bar: test2 foo: test1',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_irr()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'D' => null,
],
2 => [
'A' => 'foo',
'B' => '[matrix.0.b] [= matrix.0.b]',
'C' => '[matrix.0.c.0] [=matrix.c]',
'D' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => 'foo: [foo] bar: [bar]',
'D' => 'bar: [bar] foo: [foo]',
],
],
'data' => [
'matrix' => [
['b' => 'b1', 'c' => ['c1', 'd1', 'e1']],
['b' => 'b2', 'c' => ['c2', 'd2']],
['c' => ['c3', 'd3', 'e3']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'D' => null,
],
2 => [
'A' => 'foo',
'B' => '[matrix.b]',
'C' => '[matrix.c]',
'D' => null,
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'C' => 'foo: [foo] bar: [bar]',
'D' => 'bar: [bar] foo: [foo]',
],
],
'data' => [
'matrix' => [
['b' => 'b1', 'c' => ['c1', 'd1', 'e1']],
['b' => 'b2', 'c' => ['c2', 'd2']],
['c' => ['c3', 'd3', 'e3']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
],
2 => [
'A' => 'foo',
'B' => 'b1',
'C' => 'c1',
'D' => 'd1',
'E' => 'e1',
],
3 => [
'A' => 'foo',
'B' => 'b2',
'C' => 'c2',
'D' => 'd2',
'E' => null,
],
4 => [
'A' => 'foo',
'B' => null,
'C' => 'c3',
'D' => 'd3',
'E' => 'e3',
],
5 => [
'B' => 'test2',
'C' => 'foo: test1 bar: test2',
'D' => 'bar: test2 foo: test1',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'B2', 'to' => 'B3:B4'],
['from' => 'C2', 'to' => 'C3:C4'],
['from' => 'C2', 'to' => 'D2'],
['from' => 'C2', 'to' => 'E2'],
['from' => 'D2', 'to' => 'D3:D4'],
['from' => 'E2', 'to' => 'E3:E4'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'C', 'to' => 'D'],
['from' => 'C', 'to' => 'E'],
],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_combination1()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'E' => null,
],
2 => [
'A' => 'foo',
'B' => '[rows.b]',
'C' => '[rows.c]',
'E' => '[columns.e]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'E' => null,
],
],
'data' => [
'rows' => [
['b' => 'b1', 'c' => 'c1'],
['b' => 'b2', 'c' => 'c2'],
],
'columns' => [
['e' => ['E10', 'E11', '']],
['e' => ['E20', 'E21', 'E22']],
['e' => ['E30', 'E31', 'E32']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'E' => null,
],
2 => [
'A' => 'foo',
'B' => '[rows.*.b]',
'C' => '[rows.*.c]',
'E' => '[columns.*.e.*]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'E' => null,
],
],
'data' => [
'rows' => [
['b' => 'b1', 'c' => 'c1'],
['b' => 'b2', 'c' => 'c2'],
],
'columns' => [
['e' => ['E10', 'E11', '']],
['e' => ['E20', 'E21', 'E22']],
['e' => ['E30', 'E31', 'E32']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
],
2 => [
'A' => 'foo',
'B' => 'b1',
'C' => 'c1',
'E' => 'E10',
'F' => 'E11',
'G' => null,
],
3 => [
'A' => 'foo',
'B' => 'b2',
'C' => 'c2',
'E' => 'E20',
'F' => 'E21',
'G' => 'E22',
],
4 => [
'A' => 'foo',
'B' => null,
'C' => null,
'E' => 'E30',
'F' => 'E31',
'G' => 'E32',
],
5 => [
'B' => 'test2',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'B2', 'to' => 'B3:B4'],
['from' => 'C2', 'to' => 'C3:C4'],
['from' => 'D2', 'to' => 'D3:D4'],
['from' => 'E2', 'to' => 'E3:E4'],
['from' => 'E2', 'to' => 'F2'],
['from' => 'E2', 'to' => 'G2'],
['from' => 'F2', 'to' => 'F3:F4'],
['from' => 'G2', 'to' => 'G3:G4'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'E', 'to' => 'F'],
['from' => 'E', 'to' => 'G'],
],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_combination1_limit()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'E' => null,
],
2 => [
'A' => 'foo',
'B' => '[rows.0.b]',
'C' => '[rows.0.c]',
'E' => '[columns.0.e.0]',
],
3 => [
'A' => 'bar',
'B' => '[bar]',
'E' => null,
],
],
'data' => [
'rows' => [
['b' => 'b1', 'c' => 'c1'],
['b' => 'b2', 'c' => 'c2'],
],
'columns' => [
['e' => ['E10', 'E11', '']],
['e' => ['E20', 'E21', 'E22']],
['e' => ['E30', 'E31', 'E32']],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
],
2 => [
'B' => 'b1',
'C' => 'c1',
'E' => 'E10',
],
3 => [
'B' => 'test2',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_combination2()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'E' => '[columns.one]',
],
2 => [
'A' => 'foo',
'B' => '[rows.b]',
'C' => '[rows.c]',
'E' => '[columns.two]',
],
3 => [
'A' => '[foo]',
'E' => null,
],
],
'data' => [
'rows' => [
['b' => 'b1', 'c' => 'c1'],
['b' => 'b2', 'c' => 'c2'],
],
'columns' => [
[
'one' => ['01', '02', '03'],
'two' => [15000, 20000, 30000],
],
],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['C2:D2'],
],
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'E' => '[columns.*.one]',
],
2 => [
'A' => 'foo',
'B' => '[rows.b]',
'C' => '[rows.c]',
'E' => '[columns.*.two.*]',
],
3 => [
'A' => '[foo]',
'E' => null,
],
],
'data' => [
'rows' => [
['b' => 'b1', 'c' => 'c1'],
['b' => 'b2', 'c' => 'c2'],
],
'columns' => [
[
'one' => ['01', '02', '03'],
'two' => [15000, 20000, 30000],
],
],
'foo' => 'test1',
'bar' => 'test2',
],
'merge_cells' => ['C2:D2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'E' => '01',
'F' => '02',
'G' => '03',
],
2 => [
'A' => 'foo',
'B' => 'b1',
'C' => 'c1',
'E' => 15000,
'F' => 20000,
'G' => 30000,
],
3 => [
'A' => 'foo',
'B' => 'b2',
'C' => 'c2',
'E' => null,
'F' => null,
'G' => null,
],
4 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 1],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3'],
['from' => 'B2', 'to' => 'B3'],
['from' => 'C2', 'to' => 'C3'],
['from' => 'E1', 'to' => 'F1'],
['from' => 'E1', 'to' => 'G1'],
['from' => 'E2', 'to' => 'E3'],
['from' => 'E2', 'to' => 'F2'],
['from' => 'E2', 'to' => 'G2'],
['from' => 'F2', 'to' => 'F3'],
['from' => 'G2', 'to' => 'G3'],
],
'merge_cells' => ['C3:D3'],
'copy_width' => [
['from' => 'E', 'to' => 'F'],
['from' => 'E', 'to' => 'G'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_combination2_limit()
{
$data = [
[
'values' => [
1 => [
'A' => 'foo',
'B' => '[foo]',
'E' => '[columns.*.one.*]',
],
2 => [
'A' => 'foo',
'B' => '[rows.1.b]',
'C' => '[rows.1.c]',
'E' => '[columns.*.two.*]',
],
3 => [
'A' => '[foo]',
'E' => null,
],
],
'data' => [
'rows' => [
['b' => 'b1', 'c' => 'c1'],
['b' => 'b2', 'c' => 'c2'],
],
'columns' => [
[
'one' => ['01', '02', '03'],
'two' => [15000, 20000, 30000],
],
],
'foo' => 'test1',
'bar' => 'test2',
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'test1',
'E' => '01',
'F' => '02',
'G' => '03',
],
2 => [
'B' => 'b2',
'C' => 'c2',
'E' => 15000,
'F' => 20000,
'G' => 30000,
],
3 => [
'A' => 'test1',
],
],
'rows' => [],
'copy_style' => [
['from' => 'E1', 'to' => 'F1'],
['from' => 'E1', 'to' => 'G1'],
['from' => 'E2', 'to' => 'F2'],
['from' => 'E2', 'to' => 'G2'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'E', 'to' => 'F'],
['from' => 'E', 'to' => 'G'],
],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_combination3()
{
$data = [
[
'values' => [
1 => [
'B' => '[title] [=column.month]',
'C' => '=D6 [oops]',
'D' => 'D6',
'Q' => null,
],
3 => [
'G' => '[column.month] [= column.month]',
'Q' => null,
],
4 => [
'A' => '[list.name]',
'B' => '[list.count]',
'C' => 'kg',
'D' => '[list.price]',
'E' => '[comment]',
'G' => '[column.amount]',
'Q' => '=A1+B3+C4+D6',
],
6 => [
'B' => '[total.count]',
'Q' => null,
],
7 => [
'C' => '=D6 [oops]',
'D' => 'D6',
'Q' => null,
],
],
'data' => [
'title' => 'foo',
'total' => ['count' => 3],
'comment' => 'bar',
'list' => [
['name' => 'Product 1', 'count' => 2, 'price' => 753.14],
['name' => 'Product 2', 'count' => 1, 'price' => 123],
],
'column' => [
[
'month' => ['01', '02', '03'],
'amount' => [15000, 20000, 30000],
],
],
],
'merge_cells' => ['G4:H4'],
],
[
'values' => [
1 => [
'B' => '[title] [=column.*.month]',
'C' => '=D6',
'D' => 'D6',
'Q' => null,
],
3 => [
'G' => '[column.*.month] [= column.*.month.*]',
'Q' => null,
],
4 => [
'A' => '[list.*.name]',
'B' => '[list.*.count]',
'C' => 'kg',
'D' => '[list.price]',
'E' => '[comment]',
'G' => '[column.*.amount.*]',
'Q' => '=A1+B3+C4+D6',
],
6 => [
'B' => '[total.count]',
'Q' => null,
],
7 => [
'C' => '=D6 [oops]',
'D' => 'D6',
'Q' => null,
],
],
'data' => [
'title' => 'foo',
'total' => ['count' => 3],
'comment' => 'bar',
'list' => [
['name' => 'Product 1', 'count' => 2, 'price' => 753.14],
['name' => 'Product 2', 'count' => 1, 'price' => 123],
],
'column' => [
[
'month' => ['01', '02', '03'],
'amount' => [15000, 20000, 30000],
],
],
],
'merge_cells' => ['G4:H4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'foo',
'C' => '=D7',
],
3 => [
'G' => '01',
'H' => '02',
'I' => '03',
],
4 => [
'A' => 'Product 1',
'B' => 2,
'C' => 'kg',
'D' => 753.14,
'E' => 'bar',
'G' => 15000,
'I' => 20000,
'K' => 30000,
'Q' => '=A1+B3+C4+D7',
],
5 => [
'A' => 'Product 2',
'B' => 1,
'C' => 'kg',
'D' => 123,
'E' => 'bar',
'G' => null,
'I' => null,
'K' => null,
'Q' => '=A1+B3+C5+D7',
],
7 => [
'B' => 3,
],
8 => [
'C' => '=D7',
],
],
'rows' => [
['action' => 'add', 'row' => 5, 'qty' => 1],
],
'copy_style' => [
['from' => 'A4', 'to' => 'A5'],
['from' => 'B4', 'to' => 'B5'],
['from' => 'C4', 'to' => 'C5'],
['from' => 'D4', 'to' => 'D5'],
['from' => 'E4', 'to' => 'E5'],
['from' => 'F4', 'to' => 'F5'],
['from' => 'G3', 'to' => 'H3'],
['from' => 'G3', 'to' => 'I3'],
['from' => 'G4', 'to' => 'G5'],
['from' => 'G4', 'to' => 'I4'],
['from' => 'G4', 'to' => 'K4'],
['from' => 'I4', 'to' => 'I5'],
['from' => 'K4', 'to' => 'K5'],
['from' => 'M4', 'to' => 'M5'],
['from' => 'N4', 'to' => 'N5'],
['from' => 'O4', 'to' => 'O5'],
['from' => 'P4', 'to' => 'P5'],
['from' => 'Q4', 'to' => 'Q5'],
],
'merge_cells' => [
'I4:J4', 'K4:L4',
'G5:H5', 'I5:J5', 'K5:L5',
],
'copy_width' => [
['from' => 'G', 'to' => 'H'],
['from' => 'G', 'to' => 'I'],
['from' => 'G', 'to' => 'K'],
['from' => 'H', 'to' => 'J'],
['from' => 'H', 'to' => 'L'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_combination3_limit()
{
$data = [
[
'values' => [
1 => [
'B' => '[title]',
'G' => null,
],
3 => [
'G' => '[column.*.month.*] [= column.0.month]',
],
4 => [
'A' => '[list.0.name] [=column.0.month.0]',
'B' => '[list.0.count]',
'C' => 'kg',
'D' => '[list.0.price]',
'E' => '[comment]',
'G' => '[column.0.amount.0]',
],
6 => [
'B' => '[total.count]',
'G' => null,
],
],
'data' => [
'title' => 'foo',
'total' => ['count' => 3],
'comment' => 'bar',
'list' => [
['name' => 'Product 1', 'count' => 2, 'price' => 753.14],
['name' => 'Product 2', 'count' => 1, 'price' => 123],
],
'column' => [
[
'month' => ['01', '02', '03'],
'amount' => [15000, 20000, 30000],
],
],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'foo',
],
3 => [
'G' => '01',
'H' => '02',
'I' => '03',
],
4 => [
'A' => 'Product 1',
'B' => 2,
'D' => 753.14,
'E' => 'bar',
'G' => 15000,
],
6 => [
'B' => 3,
],
],
'rows' => [],
'copy_style' => [
['from' => 'G3', 'to' => 'H3'],
['from' => 'G3', 'to' => 'I3'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'G', 'to' => 'H'],
['from' => 'G', 'to' => 'I'],
],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_table_with_formula1()
{
$data = [
[
'values' => [
1 => [
'A' => '=A4',
'B' => '=A3',
'C' => '=A2',
'D' => '=A1',
'E' => '=A2:A2',
'F' => '=B2:G2',
],
2 => [
'A' => '[table.price]',
'B' => '[table.count]',
'C' => '=A2*B2',
'D' => '=A1+A3+A4',
'E' => 'A1+A3+A4',
'F' => null,
],
3 => [
'A' => '=A4',
'B' => '=A3',
'C' => '=A2',
'D' => '=A1',
'E' => '=A2:A2',
'F' => '=B2:G2',
],
],
'data' => [
'table' => [
['price' => 11, 'count' => 1],
['price' => 12, 'count' => 2],
['price' => 13, 'count' => 3],
['price' => 14, 'count' => 4],
],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=A7',
'B' => '=A6',
'C' => '=A2',
'D' => '=A1',
'E' => '=A2:A5',
'F' => '=B2:G5',
],
2 => [
'A' => 11,
'B' => 1,
'C' => '=A2*B2',
'D' => '=A1+A6+A7',
'E' => 'A1+A3+A4',
],
3 => [
'A' => 12,
'B' => 2,
'C' => '=A3*B3',
'D' => '=A1+A6+A7',
'E' => 'A1+A3+A4',
],
4 => [
'A' => 13,
'B' => 3,
'C' => '=A4*B4',
'D' => '=A1+A6+A7',
'E' => 'A1+A3+A4',
],
5 => [
'A' => 14,
'B' => 4,
'C' => '=A5*B5',
'D' => '=A1+A6+A7',
'E' => 'A1+A3+A4',
],
6 => [
'A' => '=A7',
'B' => '=A6',
'C' => '=A2',
'D' => '=A1',
'E' => '=A2:A5',
'F' => '=B2:G5',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 3],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A5'],
['from' => 'B2', 'to' => 'B3:B5'],
['from' => 'C2', 'to' => 'C3:C5'],
['from' => 'D2', 'to' => 'D3:D5'],
['from' => 'E2', 'to' => 'E3:E5'],
['from' => 'F2', 'to' => 'F3:F5'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_table_with_formula2()
{
$data = [
[
'values' => [
1 => [
'A' => '=A1',
'B' => '=A2',
'C' => '=A3',
'D' => '=A4',
'E' => '=A5',
'F' => '=A6',
'G' => '=A7',
'H' => '=A8',
'J' => '=A3:A3',
'K' => '=A3:C3',
],
2 => [
'A' => '[= hello]',
'K' => null,
],
3 => [
'A' => '[table.price]',
'B' => '[table.count]',
'C' => '=A3*B3',
'K' => null,
],
4 => [
'A' => '[=hello]',
'K' => null,
],
5 => [
'A' => '[!table]',
'K' => null,
],
6 => [
'A' => '[!table.count]',
'K' => null,
],
7 => [
'A' => '=A1',
'B' => '=A2',
'C' => '=A3',
'D' => '=A4',
'E' => '=A5',
'F' => '=A6',
'G' => '=A7',
'H' => '=A8',
'J' => '=A3:A3',
'K' => '=A3:C3',
],
],
'data' => [
'table' => [
['price' => 11, 'count' => 1],
['price' => 12, 'count' => 2],
['price' => 13, 'count' => 3],
['price' => 14, 'count' => 4],
['price' => 15, 'count' => 5],
],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=A1',
'B' => '=A2',
'C' => '=A2',
'D' => '=A7',
'E' => '=A7',
'F' => '=A7',
'G' => '=A7',
'H' => '=A8',
'J' => '=A2:A6',
'K' => '=A2:C6',
],
2 => [
'A' => 11,
'B' => 1,
'C' => '=A2*B2',
],
3 => [
'A' => 12,
'B' => 2,
'C' => '=A3*B3',
],
4 => [
'A' => 13,
'B' => 3,
'C' => '=A4*B4',
],
5 => [
'A' => 14,
'B' => 4,
'C' => '=A5*B5',
],
6 => [
'A' => 15,
'B' => 5,
'C' => '=A6*B6',
],
7 => [
'A' => '=A1',
'B' => '=A2',
'C' => '=A2',
'D' => '=A7',
'E' => '=A7',
'F' => '=A7',
'G' => '=A7',
'H' => '=A8',
'J' => '=A2:A6',
'K' => '=A2:C6',
],
],
'rows' => [
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'add', 'row' => 3, 'qty' => 4],
['action' => 'delete', 'row' => 7, 'qty' => 1],
['action' => 'delete', 'row' => 7, 'qty' => 1],
['action' => 'delete', 'row' => 7, 'qty' => 1],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A6'],
['from' => 'B2', 'to' => 'B3:B6'],
['from' => 'C2', 'to' => 'C3:C6'],
['from' => 'D2', 'to' => 'D3:D6'],
['from' => 'E2', 'to' => 'E3:E6'],
['from' => 'F2', 'to' => 'F3:F6'],
['from' => 'G2', 'to' => 'G3:G6'],
['from' => 'H2', 'to' => 'H3:H6'],
['from' => 'I2', 'to' => 'I3:I6'],
['from' => 'J2', 'to' => 'J3:J6'],
['from' => 'K2', 'to' => 'K3:K6'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_table_with_formula3()
{
$data = [
[
'values' => [
1 => [
'A' => '=AA1',
'B' => '=BB2',
'C' => '=CC3',
'D' => '=DD4',
'E' => '=EE5',
'F' => '=FF6',
'G' => '=A3:A3',
],
2 => [
'A' => '[= hello]',
'G' => null,
],
3 => [
'A' => '[table.price]',
'B' => '[table.count]',
'C' => '=A3*B3+C1+D2+E4+F5',
'G' => null,
],
4 => [
'A' => '=AA1',
'B' => '=BB2',
'C' => '=CC3',
'D' => '=DD4',
'E' => '=EE5',
'F' => '=FF6',
'G' => '=A3:A3',
],
],
'data' => [
'table' => [
['price' => 11, 'count' => 1],
['price' => 12, 'count' => 2],
['price' => 13, 'count' => 3],
['price' => 14, 'count' => 4],
['price' => 15, 'count' => 5],
],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=AA1',
'B' => '=BB2',
'C' => '=CC2',
'D' => '=DD7',
'E' => '=EE8',
'F' => '=FF9',
'G' => '=A2:A6',
],
2 => [
'A' => 11,
'B' => 1,
'C' => '=A2*B2+C1+D2+E7+F8',
],
3 => [
'A' => 12,
'B' => 2,
'C' => '=A3*B3+C1+D3+E7+F8',
],
4 => [
'A' => 13,
'B' => 3,
'C' => '=A4*B4+C1+D4+E7+F8',
],
5 => [
'A' => 14,
'B' => 4,
'C' => '=A5*B5+C1+D5+E7+F8',
],
6 => [
'A' => 15,
'B' => 5,
'C' => '=A6*B6+C1+D6+E7+F8',
],
7 => [
'A' => '=AA1',
'B' => '=BB2',
'C' => '=CC2',
'D' => '=DD7',
'E' => '=EE8',
'F' => '=FF9',
'G' => '=A2:A6',
],
],
'rows' => [
['action' => 'delete', 'row' => 2, 'qty' => 1],
['action' => 'add', 'row' => 3, 'qty' => 4],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A6'],
['from' => 'B2', 'to' => 'B3:B6'],
['from' => 'C2', 'to' => 'C3:C6'],
['from' => 'D2', 'to' => 'D3:D6'],
['from' => 'E2', 'to' => 'E3:E6'],
['from' => 'F2', 'to' => 'F3:F6'],
['from' => 'G2', 'to' => 'G3:G6'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_1x1()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
],
2 => [
'A' => '[matrix]',
],
3 => [
'A' => '[bar]',
],
],
'data' => [
'foo' => 'test1',
'bar' => 'test2',
'matrix' => [
[['one', 'two', 'three', 'four']],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'test1',
],
2 => [
'A' => 'one',
'B' => 'two',
'C' => 'three',
'D' => 'four',
],
3 => [
'A' => 'test2',
],
],
'rows' => [],
'copy_style' => [
['from' => 'A2', 'to' => 'B2'],
['from' => 'A2', 'to' => 'C2'],
['from' => 'A2', 'to' => 'D2'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'A', 'to' => 'B'],
['from' => 'A', 'to' => 'C'],
['from' => 'A', 'to' => 'D'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_1x2()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
],
2 => [
'A' => '[matrix]',
],
3 => [
'A' => '[bar]',
],
],
'data' => [
'foo' => 'test1',
'bar' => 'test2',
'matrix' => [
[['one', 'two', 'three', 'four']],
],
],
'merge_cells' => ['A2:B2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'test1',
],
2 => [
'A' => 'one',
'C' => 'two',
'E' => 'three',
'G' => 'four',
],
3 => [
'A' => 'test2',
],
],
'rows' => [],
'copy_style' => [
['from' => 'A2', 'to' => 'C2'],
['from' => 'A2', 'to' => 'E2'],
['from' => 'A2', 'to' => 'G2'],
],
'merge_cells' => ['C2:D2', 'E2:F2', 'G2:H2'],
'copy_width' => [
['from' => 'A', 'to' => 'C'],
['from' => 'A', 'to' => 'E'],
['from' => 'A', 'to' => 'G'],
['from' => 'B', 'to' => 'D'],
['from' => 'B', 'to' => 'F'],
['from' => 'B', 'to' => 'H'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_1x3()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
],
2 => [
'A' => '[matrix]',
],
3 => [
'A' => '[bar]',
],
],
'data' => [
'foo' => 'test1',
'bar' => 'test2',
'matrix' => [
[['one', 'two', 'three', 'four']],
],
],
'merge_cells' => ['A2:C2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'test1',
],
2 => [
'A' => 'one',
'D' => 'two',
'G' => 'three',
'J' => 'four',
],
3 => [
'A' => 'test2',
],
],
'rows' => [],
'copy_style' => [
['from' => 'A2', 'to' => 'D2'],
['from' => 'A2', 'to' => 'G2'],
['from' => 'A2', 'to' => 'J2'],
],
'merge_cells' => ['D2:F2', 'G2:I2', 'J2:L2'],
'copy_width' => [
['from' => 'A', 'to' => 'D'],
['from' => 'A', 'to' => 'G'],
['from' => 'A', 'to' => 'J'],
['from' => 'B', 'to' => 'E'],
['from' => 'B', 'to' => 'H'],
['from' => 'B', 'to' => 'K'],
['from' => 'C', 'to' => 'F'],
['from' => 'C', 'to' => 'I'],
['from' => 'C', 'to' => 'L'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_3x1()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
],
2 => [
'A' => '[matrix]',
],
3 => [
'A' => '[bar]',
],
],
'data' => [
'foo' => 'test1',
'bar' => 'test2',
'matrix' => [
[['one', 'two', 'three', 'four']],
[['one-b', 'two-b', 'three-b', 'four-b']],
[['one-c', 'two-c', 'three-c', 'four-c']],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'test1',
],
2 => [
'A' => 'one',
'B' => 'two',
'C' => 'three',
'D' => 'four',
],
3 => [
'A' => 'one-b',
'B' => 'two-b',
'C' => 'three-b',
'D' => 'four-b',
],
4 => [
'A' => 'one-c',
'B' => 'two-c',
'C' => 'three-c',
'D' => 'four-c',
],
5 => [
'A' => 'test2',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'A2', 'to' => 'B2'],
['from' => 'A2', 'to' => 'C2'],
['from' => 'A2', 'to' => 'D2'],
['from' => 'B2', 'to' => 'B3:B4'],
['from' => 'C2', 'to' => 'C3:C4'],
['from' => 'D2', 'to' => 'D3:D4'],
],
'merge_cells' => [],
'copy_width' => [
['from' => 'A', 'to' => 'B'],
['from' => 'A', 'to' => 'C'],
['from' => 'A', 'to' => 'D'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_3x2()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
],
2 => [
'A' => '[matrix]',
],
3 => [
'A' => '[bar]',
],
],
'data' => [
'foo' => 'test1',
'bar' => 'test2',
'matrix' => [
[['one', 'two', 'three', 'four']],
[['one-b', 'two-b', 'three-b', 'four-b']],
[['one-c', 'two-c', 'three-c', 'four-c']],
],
],
'merge_cells' => ['A2:B2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'test1',
],
2 => [
'A' => 'one',
'C' => 'two',
'E' => 'three',
'G' => 'four',
],
3 => [
'A' => 'one-b',
'C' => 'two-b',
'E' => 'three-b',
'G' => 'four-b',
],
4 => [
'A' => 'one-c',
'C' => 'two-c',
'E' => 'three-c',
'G' => 'four-c',
],
5 => [
'A' => 'test2',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'A2', 'to' => 'C2'],
['from' => 'A2', 'to' => 'E2'],
['from' => 'A2', 'to' => 'G2'],
['from' => 'C2', 'to' => 'C3:C4'],
['from' => 'E2', 'to' => 'E3:E4'],
['from' => 'G2', 'to' => 'G3:G4'],
],
'merge_cells' => [
'C2:D2', 'E2:F2', 'G2:H2',
'A3:B3', 'C3:D3', 'E3:F3', 'G3:H3',
'A4:B4', 'C4:D4', 'E4:F4', 'G4:H4',
],
'copy_width' => [
['from' => 'A', 'to' => 'C'],
['from' => 'A', 'to' => 'E'],
['from' => 'A', 'to' => 'G'],
['from' => 'B', 'to' => 'D'],
['from' => 'B', 'to' => 'F'],
['from' => 'B', 'to' => 'H'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_3x3()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo]',
],
2 => [
'A' => '[matrix]',
],
3 => [
'A' => '[bar]',
],
],
'data' => [
'foo' => 'test1',
'bar' => 'test2',
'matrix' => [
[['one', 'two', 'three', 'four']],
[['one-b', 'two-b', 'three-b', 'four-b']],
[['one-c', 'two-c', 'three-c', 'four-c']],
],
],
'merge_cells' => ['A2:C2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'test1',
],
2 => [
'A' => 'one',
'D' => 'two',
'G' => 'three',
'J' => 'four',
],
3 => [
'A' => 'one-b',
'D' => 'two-b',
'G' => 'three-b',
'J' => 'four-b',
],
4 => [
'A' => 'one-c',
'D' => 'two-c',
'G' => 'three-c',
'J' => 'four-c',
],
5 => [
'A' => 'test2',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3:A4'],
['from' => 'A2', 'to' => 'D2'],
['from' => 'A2', 'to' => 'G2'],
['from' => 'A2', 'to' => 'J2'],
['from' => 'D2', 'to' => 'D3:D4'],
['from' => 'G2', 'to' => 'G3:G4'],
['from' => 'J2', 'to' => 'J3:J4'],
],
'merge_cells' => [
'D2:F2', 'G2:I2', 'J2:L2',
'A3:C3', 'D3:F3', 'G3:I3', 'J3:L3',
'A4:C4', 'D4:F4', 'G4:I4', 'J4:L4',
],
'copy_width' => [
['from' => 'A', 'to' => 'D'],
['from' => 'A', 'to' => 'G'],
['from' => 'A', 'to' => 'J'],
['from' => 'B', 'to' => 'E'],
['from' => 'B', 'to' => 'H'],
['from' => 'B', 'to' => 'K'],
['from' => 'C', 'to' => 'F'],
['from' => 'C', 'to' => 'I'],
['from' => 'C', 'to' => 'L'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_multi1()
{
$data = [
[
'values' => [
2 => [
'C' => '[project.months]',
],
3 => [
'A' => '[project.name]',
'C' => '[project.amount]',
],
],
'data' => [
'project' => [
'name' => ['N1', 'N2', 'N3'],
'months' => [['01', '02', '03']],
'amount' => [
[101, 201, 301],
[102, 202, 302],
[103, 203, 303],
],
],
],
'merge_cells' => ['C2:D2', 'A3:B3', 'C3:D3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
2 => [
'C' => '01',
'E' => '02',
'G' => '03',
],
3 => [
'A' => 'N1',
'C' => 101,
'E' => 201,
'G' => 301,
],
4 => [
'A' => 'N2',
'C' => 102,
'E' => 202,
'G' => 302,
],
5 => [
'A' => 'N3',
'C' => 103,
'E' => 203,
'G' => 303,
],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 2],
],
'copy_style' => [
['from' => 'A3', 'to' => 'A4:A5'],
['from' => 'C2', 'to' => 'E2'],
['from' => 'C2', 'to' => 'G2'],
['from' => 'C3', 'to' => 'C4:C5'],
['from' => 'C3', 'to' => 'E3'],
['from' => 'C3', 'to' => 'G3'],
['from' => 'E3', 'to' => 'E4:E5'],
['from' => 'G3', 'to' => 'G4:G5'],
],
'merge_cells' => [
'E2:F2', 'G2:H2',
'E3:F3', 'G3:H3',
'A4:B4', 'C4:D4', 'E4:F4', 'G4:H4',
'A5:B5', 'C5:D5', 'E5:F5', 'G5:H5',
],
'copy_width' => [
['from' => 'C', 'to' => 'E'],
['from' => 'C', 'to' => 'G'],
['from' => 'D', 'to' => 'F'],
['from' => 'D', 'to' => 'H'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_matrix_merge_multi2()
{
$data = [
[
'values' => [
2 => [
'D' => '[project.months]',
],
3 => [
'A' => '[project.name]',
'D' => '[project.amount]',
],
],
'data' => [
'project' => [
'name' => ['N1', 'N2', 'N3'],
'months' => [['01', '02', '03']],
'amount' => [
[101, 201, 301],
[102, 202, 302],
[103, 203, 303],
],
],
],
'merge_cells' => ['D2:F2', 'A3:C3', 'D3:F3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
2 => [
'D' => '01',
'G' => '02',
'J' => '03',
],
3 => [
'A' => 'N1',
'D' => 101,
'G' => 201,
'J' => 301,
],
4 => [
'A' => 'N2',
'D' => 102,
'G' => 202,
'J' => 302,
],
5 => [
'A' => 'N3',
'D' => 103,
'G' => 203,
'J' => 303,
],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 2],
],
'copy_style' => [
['from' => 'A3', 'to' => 'A4:A5'],
['from' => 'D2', 'to' => 'G2'],
['from' => 'D2', 'to' => 'J2'],
['from' => 'D3', 'to' => 'D4:D5'],
['from' => 'D3', 'to' => 'G3'],
['from' => 'D3', 'to' => 'J3'],
['from' => 'G3', 'to' => 'G4:G5'],
['from' => 'J3', 'to' => 'J4:J5'],
],
'merge_cells' => [
'G2:I2', 'J2:L2',
'G3:I3', 'J3:L3',
'A4:C4', 'D4:F4', 'G4:I4', 'J4:L4',
'A5:C5', 'D5:F5', 'G5:I5', 'J5:L5',
],
'copy_width' => [
['from' => 'D', 'to' => 'G'],
['from' => 'D', 'to' => 'J'],
['from' => 'E', 'to' => 'H'],
['from' => 'E', 'to' => 'K'],
['from' => 'F', 'to' => 'I'],
['from' => 'F', 'to' => 'L'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_0x2()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => null,
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'C' => null,
],
3 => [
'B' => '[project.amount_1]',
'C' => '[project.amount_2]',
],
4 => [
'A' => '[foo]',
'C' => null,
],
],
'data' => [
'project' => [],
'foo' => 'test1',
],
'merge_cells' => ['A2:A3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
],
2 => [
'A' => null,
'B' => null,
],
3 => [
'B' => null,
'C' => null,
],
4 => [
'A' => 'test1',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_1x2()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => null,
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'C' => null,
],
3 => [
'B' => '[project.amount_1]',
'C' => '[project.amount_2]',
],
4 => [
'A' => '[foo]',
'C' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
'C' => 102,
],
4 => [
'A' => 'test1',
],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_2x2()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => null,
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'C' => null,
],
3 => [
'B' => '[project.amount_1]',
'C' => '[project.amount_2]',
],
4 => [
'A' => '[foo]',
'C' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
['id' => 2, 'name' => 'project 2', 'amount_1' => 201, 'amount_2' => 202],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
'C' => 102,
],
4 => [
'A' => 2,
'B' => 'project 2',
],
5 => [
'B' => 201,
'C' => 202,
],
6 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 2],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A4'],
['from' => 'B2', 'to' => 'B4'],
['from' => 'B3', 'to' => 'B5'],
['from' => 'C2', 'to' => 'C4'],
['from' => 'C3', 'to' => 'C5'],
],
'merge_cells' => [
'A4:A5',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_2x2_skip()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B2:B2)',
],
2 => [
'A' => 'Hello',
'B' => '[project.name]',
],
4 => [
'A' => '[foo]',
'B' => null,
],
],
'data' => [
'project' => [
['name' => 'project 1'],
['name' => 'project 2'],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A3)',
'B' => '=SUM(B2:B3)',
],
2 => [
'A' => 'Hello',
'B' => 'project 1',
],
3 => [
'B' => 'project 2',
],
5 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 1],
],
'copy_style' => [
['from' => 'B2', 'to' => 'B3'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_3x2()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => null,
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'C' => null,
],
3 => [
'B' => '[project.amount_1]',
'C' => '[project.amount_2]',
],
4 => [
'A' => '[foo]',
'C' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
['id' => 2, 'name' => 'project 2', 'amount_1' => 201, 'amount_2' => 202],
['id' => 3, 'name' => 'project 3', 'amount_1' => 301, 'amount_2' => 302],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
'C' => 102,
],
4 => [
'A' => 2,
'B' => 'project 2',
],
5 => [
'B' => 201,
'C' => 202,
],
6 => [
'A' => 3,
'B' => 'project 3',
],
7 => [
'B' => 301,
'C' => 302,
],
8 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 4],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A4'],
['from' => 'A2', 'to' => 'A6'],
['from' => 'B2', 'to' => 'B4'],
['from' => 'B2', 'to' => 'B6'],
['from' => 'B3', 'to' => 'B5'],
['from' => 'B3', 'to' => 'B7'],
['from' => 'C2', 'to' => 'C4'],
['from' => 'C2', 'to' => 'C6'],
['from' => 'C3', 'to' => 'C5'],
['from' => 'C3', 'to' => 'C7'],
],
'merge_cells' => [
'A4:A5', 'A6:A7',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_4x2()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => null,
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'C' => null,
],
3 => [
'B' => '[project.amount_1]',
'C' => '[project.amount_2]',
],
4 => [
'A' => '[foo]',
'C' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
['id' => 2, 'name' => 'project 2', 'amount_1' => 201, 'amount_2' => 202],
['id' => 3, 'name' => 'project 3', 'amount_1' => 301, 'amount_2' => 302],
['id' => 4, 'name' => 'project 4', 'amount_1' => 401, 'amount_2' => 402],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
'C' => 102,
],
4 => [
'A' => 2,
'B' => 'project 2',
],
5 => [
'B' => 201,
'C' => 202,
],
6 => [
'A' => 3,
'B' => 'project 3',
],
7 => [
'B' => 301,
'C' => 302,
],
8 => [
'A' => 4,
'B' => 'project 4',
],
9 => [
'B' => 401,
'C' => 402,
],
10 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 6],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A4'],
['from' => 'A2', 'to' => 'A6'],
['from' => 'A2', 'to' => 'A8'],
['from' => 'B2', 'to' => 'B4'],
['from' => 'B2', 'to' => 'B6'],
['from' => 'B2', 'to' => 'B8'],
['from' => 'B3', 'to' => 'B5'],
['from' => 'B3', 'to' => 'B7'],
['from' => 'B3', 'to' => 'B9'],
['from' => 'C2', 'to' => 'C4'],
['from' => 'C2', 'to' => 'C6'],
['from' => 'C2', 'to' => 'C8'],
['from' => 'C3', 'to' => 'C5'],
['from' => 'C3', 'to' => 'C7'],
['from' => 'C3', 'to' => 'C9'],
],
'merge_cells' => [
'A4:A5', 'A6:A7', 'A8:A9',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_4x3()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => '=SUM(C4:C4)',
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'C' => null,
],
3 => [
'B' => '[project.amount_1]',
'C' => null,
],
4 => [
'C' => '[project.amount_2]',
],
6 => [
'A' => '[foo]',
'C' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
['id' => 2, 'name' => 'project 2', 'amount_1' => 201, 'amount_2' => 202],
['id' => 3, 'name' => 'project 3', 'amount_1' => 301, 'amount_2' => 302],
['id' => 4, 'name' => 'project 4', 'amount_1' => 401, 'amount_2' => 402],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => '=SUM(C4:C4)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
],
4 => [
'C' => 102,
],
5 => [
'A' => 2,
'B' => 'project 2',
],
6 => [
'B' => 201,
],
7 => [
'C' => 202,
],
8 => [
'A' => 3,
'B' => 'project 3',
],
9 => [
'B' => 301,
],
10 => [
'C' => 302,
],
11 => [
'A' => 4,
'B' => 'project 4',
],
12 => [
'B' => 401,
],
13 => [
'C' => 402,
],
15 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 5, 'qty' => 9],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A5'],
['from' => 'A2', 'to' => 'A8'],
['from' => 'A2', 'to' => 'A11'],
['from' => 'B2', 'to' => 'B5'],
['from' => 'B2', 'to' => 'B8'],
['from' => 'B2', 'to' => 'B11'],
['from' => 'B3', 'to' => 'B6'],
['from' => 'B3', 'to' => 'B9'],
['from' => 'B3', 'to' => 'B12'],
['from' => 'B4', 'to' => 'B7'],
['from' => 'B4', 'to' => 'B10'],
['from' => 'B4', 'to' => 'B13'],
['from' => 'C2', 'to' => 'C5'],
['from' => 'C2', 'to' => 'C8'],
['from' => 'C2', 'to' => 'C11'],
['from' => 'C3', 'to' => 'C6'],
['from' => 'C3', 'to' => 'C9'],
['from' => 'C3', 'to' => 'C12'],
['from' => 'C4', 'to' => 'C7'],
['from' => 'C4', 'to' => 'C10'],
['from' => 'C4', 'to' => 'C13'],
],
'merge_cells' => [
'A5:A7', 'A8:A10', 'A11:A13',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_4x4()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => '=SUM(C4:C4)',
'D' => '=SUM(D5:D5)',
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'D' => null,
],
3 => [
'B' => '[project.amount_1]',
'D' => null,
],
4 => [
'C' => '[project.amount_2]',
'D' => null,
],
6 => [
'A' => '[foo]',
'D' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
['id' => 2, 'name' => 'project 2', 'amount_1' => 201, 'amount_2' => 202],
['id' => 3, 'name' => 'project 3', 'amount_1' => 301, 'amount_2' => 302],
['id' => 4, 'name' => 'project 4', 'amount_1' => 401, 'amount_2' => 402],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A5'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => '=SUM(C4:C4)',
'D' => '=SUM(D5:D5)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
],
4 => [
'C' => 102,
],
6 => [
'A' => 2,
'B' => 'project 2',
],
7 => [
'B' => 201,
],
8 => [
'C' => 202,
],
10 => [
'A' => 3,
'B' => 'project 3',
],
11 => [
'B' => 301,
],
12 => [
'C' => 302,
],
14 => [
'A' => 4,
'B' => 'project 4',
],
15 => [
'B' => 401,
],
16 => [
'C' => 402,
],
18 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 6, 'qty' => 12],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A6'],
['from' => 'A2', 'to' => 'A10'],
['from' => 'A2', 'to' => 'A14'],
['from' => 'B2', 'to' => 'B6'],
['from' => 'B2', 'to' => 'B10'],
['from' => 'B2', 'to' => 'B14'],
['from' => 'B3', 'to' => 'B7'],
['from' => 'B3', 'to' => 'B11'],
['from' => 'B3', 'to' => 'B15'],
['from' => 'B4', 'to' => 'B8'],
['from' => 'B4', 'to' => 'B12'],
['from' => 'B4', 'to' => 'B16'],
['from' => 'B5', 'to' => 'B9'],
['from' => 'B5', 'to' => 'B13'],
['from' => 'B5', 'to' => 'B17'],
['from' => 'C2', 'to' => 'C6'],
['from' => 'C2', 'to' => 'C10'],
['from' => 'C2', 'to' => 'C14'],
['from' => 'C3', 'to' => 'C7'],
['from' => 'C3', 'to' => 'C11'],
['from' => 'C3', 'to' => 'C15'],
['from' => 'C4', 'to' => 'C8'],
['from' => 'C4', 'to' => 'C12'],
['from' => 'C4', 'to' => 'C16'],
['from' => 'C5', 'to' => 'C9'],
['from' => 'C5', 'to' => 'C13'],
['from' => 'C5', 'to' => 'C17'],
['from' => 'D2', 'to' => 'D6'],
['from' => 'D2', 'to' => 'D10'],
['from' => 'D2', 'to' => 'D14'],
['from' => 'D3', 'to' => 'D7'],
['from' => 'D3', 'to' => 'D11'],
['from' => 'D3', 'to' => 'D15'],
['from' => 'D4', 'to' => 'D8'],
['from' => 'D4', 'to' => 'D12'],
['from' => 'D4', 'to' => 'D16'],
['from' => 'D5', 'to' => 'D9'],
['from' => 'D5', 'to' => 'D13'],
['from' => 'D5', 'to' => 'D17'],
],
'merge_cells' => [
'A6:A9', 'A10:A13', 'A14:A17',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_4x5()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => '=SUM(C4:C4)',
'D' => '=SUM(D5:D5)',
'E' => '=SUM(E6:E6)',
],
2 => [
'A' => '[project.id]',
'B' => '[project.name]',
'E' => null,
],
3 => [
'B' => '[project.amount_1]',
'E' => null,
],
5 => [
'C' => '[project.amount_2]',
'E' => null,
],
7 => [
'A' => '[foo]',
'E' => null,
],
],
'data' => [
'project' => [
['id' => 1, 'name' => 'project 1', 'amount_1' => 101, 'amount_2' => 102],
['id' => 2, 'name' => 'project 2', 'amount_1' => 201, 'amount_2' => 202],
['id' => 3, 'name' => 'project 3', 'amount_1' => 301, 'amount_2' => 302],
['id' => 4, 'name' => 'project 4', 'amount_1' => 401, 'amount_2' => 402],
],
'foo' => 'test1',
],
'merge_cells' => ['A2:A6'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(B3:B3)',
'C' => '=SUM(C4:C4)',
'D' => '=SUM(D5:D5)',
'E' => '=SUM(E6:E6)',
],
2 => [
'A' => 1,
'B' => 'project 1',
],
3 => [
'B' => 101,
],
5 => [
'C' => 102,
],
7 => [
'A' => 2,
'B' => 'project 2',
],
8 => [
'B' => 201,
],
10 => [
'C' => 202,
],
12 => [
'A' => 3,
'B' => 'project 3',
],
13 => [
'B' => 301,
],
15 => [
'C' => 302,
],
17 => [
'A' => 4,
'B' => 'project 4',
],
18 => [
'B' => 401,
],
20 => [
'C' => 402,
],
22 => [
'A' => 'test1',
],
],
'rows' => [
['action' => 'add', 'row' => 7, 'qty' => 15],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A7'],
['from' => 'A2', 'to' => 'A12'],
['from' => 'A2', 'to' => 'A17'],
['from' => 'B2', 'to' => 'B7'],
['from' => 'B2', 'to' => 'B12'],
['from' => 'B2', 'to' => 'B17'],
['from' => 'B3', 'to' => 'B8'],
['from' => 'B3', 'to' => 'B13'],
['from' => 'B3', 'to' => 'B18'],
['from' => 'B4', 'to' => 'B9'],
['from' => 'B4', 'to' => 'B14'],
['from' => 'B4', 'to' => 'B19'],
['from' => 'B5', 'to' => 'B10'],
['from' => 'B5', 'to' => 'B15'],
['from' => 'B5', 'to' => 'B20'],
['from' => 'B6', 'to' => 'B11'],
['from' => 'B6', 'to' => 'B16'],
['from' => 'B6', 'to' => 'B21'],
['from' => 'C2', 'to' => 'C7'],
['from' => 'C2', 'to' => 'C12'],
['from' => 'C2', 'to' => 'C17'],
['from' => 'C3', 'to' => 'C8'],
['from' => 'C3', 'to' => 'C13'],
['from' => 'C3', 'to' => 'C18'],
['from' => 'C4', 'to' => 'C9'],
['from' => 'C4', 'to' => 'C14'],
['from' => 'C4', 'to' => 'C19'],
['from' => 'C5', 'to' => 'C10'],
['from' => 'C5', 'to' => 'C15'],
['from' => 'C5', 'to' => 'C20'],
['from' => 'C6', 'to' => 'C11'],
['from' => 'C6', 'to' => 'C16'],
['from' => 'C6', 'to' => 'C21'],
['from' => 'D2', 'to' => 'D7'],
['from' => 'D2', 'to' => 'D12'],
['from' => 'D2', 'to' => 'D17'],
['from' => 'D3', 'to' => 'D8'],
['from' => 'D3', 'to' => 'D13'],
['from' => 'D3', 'to' => 'D18'],
['from' => 'D4', 'to' => 'D9'],
['from' => 'D4', 'to' => 'D14'],
['from' => 'D4', 'to' => 'D19'],
['from' => 'D5', 'to' => 'D10'],
['from' => 'D5', 'to' => 'D15'],
['from' => 'D5', 'to' => 'D20'],
['from' => 'D6', 'to' => 'D11'],
['from' => 'D6', 'to' => 'D16'],
['from' => 'D6', 'to' => 'D21'],
['from' => 'E2', 'to' => 'E7'],
['from' => 'E2', 'to' => 'E12'],
['from' => 'E2', 'to' => 'E17'],
['from' => 'E3', 'to' => 'E8'],
['from' => 'E3', 'to' => 'E13'],
['from' => 'E3', 'to' => 'E18'],
['from' => 'E4', 'to' => 'E9'],
['from' => 'E4', 'to' => 'E14'],
['from' => 'E4', 'to' => 'E19'],
['from' => 'E5', 'to' => 'E10'],
['from' => 'E5', 'to' => 'E15'],
['from' => 'E5', 'to' => 'E20'],
['from' => 'E6', 'to' => 'E11'],
['from' => 'E6', 'to' => 'E16'],
['from' => 'E6', 'to' => 'E21'],
],
'merge_cells' => [
'A7:A11', 'A12:A16', 'A17:A21',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_merge_multi()
{
$data = [
[
'values' => [
1 => [
'B' => '[names]',
],
2 => [
'A' => '[months]',
'B' => '[amounts.p1]',
],
4 => [
'B' => '[amounts.p2]',
],
'5' => [
'A' => '=A1',
'B' => null,
],
],
'data' => [
'names' => [['One', 'Two', 'Three']],
'months' => ['01', '02', '03'],
'amounts' => [
'p1' => [
['One R1 P1', 'Two R1 P1', 'Three R1 P1'],
['One R2 P1', 'Two R2 P1', 'Three R2 P1'],
['One R3 P1', 'Two R3 P1', 'Three R3 P1'],
],
'p2' => [
['One R1 P2', 'Two R1 P2', 'Three R1 P2'],
['One R2 P2', 'Two R2 P2', 'Three R2 P2'],
['One R3 P2', 'Two R3 P2', 'Three R3 P2'],
],
],
],
'merge_cells' => ['A2:A4', 'B1:C1', 'B2:C2', 'B4:C4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'One',
'D' => 'Two',
'F' => 'Three',
],
2 => [
'A' => '01',
'B' => 'One R1 P1',
'D' => 'Two R1 P1',
'F' => 'Three R1 P1',
],
4 => [
'B' => 'One R1 P2',
'D' => 'Two R1 P2',
'F' => 'Three R1 P2',
],
5 => [
'A' => '02',
'B' => 'One R2 P1',
'D' => 'Two R2 P1',
'F' => 'Three R2 P1',
],
7 => [
'B' => 'One R2 P2',
'D' => 'Two R2 P2',
'F' => 'Three R2 P2',
],
8 => [
'A' => '03',
'B' => 'One R3 P1',
'D' => 'Two R3 P1',
'F' => 'Three R3 P1',
],
10 => [
'B' => 'One R3 P2',
'D' => 'Two R3 P2',
'F' => 'Three R3 P2',
],
11 => [
'A' => '=A1',
],
],
'rows' => [
['action' => 'add', 'row' => 5, 'qty' => 6],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A5'],
['from' => 'A2', 'to' => 'A8'],
['from' => 'B1', 'to' => 'D1'],
['from' => 'B1', 'to' => 'F1'],
['from' => 'B2', 'to' => 'B5'],
['from' => 'B2', 'to' => 'B8'],
['from' => 'B2', 'to' => 'D2'],
['from' => 'B2', 'to' => 'F2'],
['from' => 'B3', 'to' => 'B6'],
['from' => 'B3', 'to' => 'B9'],
['from' => 'B4', 'to' => 'B7'],
['from' => 'B4', 'to' => 'B10'],
['from' => 'B4', 'to' => 'D4'],
['from' => 'B4', 'to' => 'F4'],
['from' => 'D2', 'to' => 'D5'],
['from' => 'D2', 'to' => 'D8'],
['from' => 'D4', 'to' => 'D7'],
['from' => 'D4', 'to' => 'D10'],
['from' => 'F2', 'to' => 'F5'],
['from' => 'F2', 'to' => 'F8'],
['from' => 'F4', 'to' => 'F7'],
['from' => 'F4', 'to' => 'F10'],
],
'merge_cells' => [
'D1:E1', 'F1:G1',
'D2:E2', 'F2:G2',
'A5:A7', 'B5:C5', 'D5:E5', 'F5:G5',
'A8:A10', 'B8:C8', 'D8:E8', 'F8:G8',
'D4:E4', 'F4:G4',
'B7:C7', 'D7:E7', 'F7:G7',
'B10:C10', 'D10:E10', 'F10:G10',
],
'copy_width' => [
['from' => 'B', 'to' => 'D'],
['from' => 'B', 'to' => 'F'],
['from' => 'C', 'to' => 'E'],
['from' => 'C', 'to' => 'G'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_shift1()
{
$data = [
[
'values' => [
1 => [
'A' => '[list.qty]',
'B' => null,
],
2 => [
'A' => '=SUM(A1:A1)',
'B' => '=SUM(A1:A10)',
],
3 => [
'A' => '-',
'B' => '[! list]',
],
],
'data' => [
'list' => [ ['qty' => 1], ['qty' => 2] ],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 1,
],
2 => [
'A' => 2,
],
3 => [
'A' => '=SUM(A1:A2)',
'B' => '=SUM(A1:A10)',
],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 1],
['action' => 'delete', 'row' => 4, 'qty' => 1],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2'],
['from' => 'B1', 'to' => 'B2'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_shift2()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(A2:A20)',
],
2 => [
'A' => '[list.qty]',
'B' => null,
],
3 => [
'A' => '=SUM(A2:A2)',
'B' => '=SUM(A2:A20)',
],
4 => [
'A' => '-',
'B' => '[! list]',
],
],
'data' => [
'list' => [ ['qty' => 1], ['qty' => 2] ],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=SUM(A2:A3)',
'B' => '=SUM(A2:A20)',
],
2 => [
'A' => 1,
],
3 => [
'A' => 2,
],
4 => [
'A' => '=SUM(A2:A3)',
'B' => '=SUM(A2:A20)',
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 1],
['action' => 'delete', 'row' => 5, 'qty' => 1],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3'],
['from' => 'B2', 'to' => 'B3'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_shift3()
{
$data = [
[
'values' => [
1 => [
'A' => '[list.qty]',
],
2 => [
'A' => '=SUM(A1:A1)',
],
],
'data' => [
'list' => [ ['qty' => 1], ['qty' => 2] ],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 1,
],
2 => [
'A' => 2,
],
3 => [
'A' => '=SUM(A1:A2)',
],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 1],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_shift4()
{
$data = [
[
'values' => [
1 => [
'A' => '=A2:A2',
],
2 => [
'A' => '[list.qty]',
],
],
'data' => [
'list' => [ ['qty' => 1], ['qty' => 2] ],
],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => '=A2:A3',
],
2 => [
'A' => 1,
],
3 => [
'A' => 2,
],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 1],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A3'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], [])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_merge1()
{
$data = [
[
'values' => [
1 => [
'A' => '[list.a]',
'B' => '[list.b]',
'C' => '[list.c]',
],
2 => [
'C' => 'Foo',
],
],
'data' => [
'list' => [
['a' => 'a1', 'b' => 'b1', 'c' => 'c1'],
['a' => 'a2', 'b' => 'b2', 'c' => 'c2'],
['a' => 'a3', 'b' => 'b3', 'c' => 'c3'],
['a' => 'a4', 'b' => 'b4', 'c' => 'c4'],
],
],
'merge_cells' => ['A1:A3', 'B1:B2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'A' => 'a1',
'B' => 'b1',
'C' => 'c1',
],
2 => [
'C' => 'Foo',
],
4 => [
'A' => 'a2',
'B' => 'b2',
'C' => 'c2',
],
5 => [
'C' => 'Foo',
],
7 => [
'A' => 'a3',
'B' => 'b3',
'C' => 'c3',
],
8 => [
'C' => 'Foo',
],
10 => [
'A' => 'a4',
'B' => 'b4',
'C' => 'c4',
],
11 => [
'C' => 'Foo',
],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 9],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A4'],
['from' => 'A1', 'to' => 'A7'],
['from' => 'A1', 'to' => 'A10'],
['from' => 'B1', 'to' => 'B4'],
['from' => 'B1', 'to' => 'B7'],
['from' => 'B1', 'to' => 'B10'],
['from' => 'C1', 'to' => 'C4'],
['from' => 'C1', 'to' => 'C7'],
['from' => 'C1', 'to' => 'C10'],
['from' => 'C2', 'to' => 'C5'],
['from' => 'C2', 'to' => 'C8'],
['from' => 'C2', 'to' => 'C11'],
],
'merge_cells' => ['A4:A6', 'B4:B5', 'A7:A9', 'B7:B8', 'A10:A12', 'B10:B11'],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_multi_merge2()
{
$data = [
[
'values' => [
1 => [
'B' => '[managers.name]',
],
2 => [
'A' => '[managers.sales.month]',
'B' => '[managers.sales.amount]',
],
3 => [
'B' => '[managers.sales.qty]',
],
4 => [
'B' => 'Hello',
],
],
'data' => [
'managers' => [
'name' => [['Liam', 'Noah', 'Emma']],
'sales' => [
['month' => '01', 'qty' => [[1, 2, 3]], 'amount' => [100, 101, 102]],
['month' => '02', 'qty' => [[1, 2, 3]], 'amount' => [200, 201, 202]],
['month' => '03', 'qty' => [[1, 2, 3]], 'amount' => [300, 301, 302]],
],
],
],
'merge_cells' => ['B1:C1', 'A2:A4', 'B2:C2', 'B3:C3', 'B4:C4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => [
'B' => 'Liam',
'D' => 'Noah',
'F' => 'Emma',
],
2 => [
'A' => '01',
'B' => 100,
'D' => 101,
'F' => 102,
],
3 => [
'B' => 1,
'D' => 2,
'F' => 3,
],
4 => [
'B' => 'Hello',
],
5 => [
'A' => '02',
'B' => 200,
'D' => 201,
'F' => 202,
],
6 => [
'B' => 1,
'D' => 2,
'F' => 3,
],
7 => [
'B' => 'Hello',
],
8 => [
'A' => '03',
'B' => 300,
'D' => 301,
'F' => 302,
],
9 => [
'B' => 1,
'D' => 2,
'F' => 3,
],
10 => [
'B' => 'Hello',
],
],
'rows' => [
['action' => 'add', 'row' => 5, 'qty' => 6],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A5'],
['from' => 'A2', 'to' => 'A8'],
['from' => 'B1', 'to' => 'D1'],
['from' => 'B1', 'to' => 'F1'],
['from' => 'B2', 'to' => 'B5'],
['from' => 'B2', 'to' => 'B8'],
['from' => 'B2', 'to' => 'D2'],
['from' => 'B2', 'to' => 'F2'],
['from' => 'B3', 'to' => 'B6'],
['from' => 'B3', 'to' => 'B9'],
['from' => 'B3', 'to' => 'D3'],
['from' => 'B3', 'to' => 'F3'],
['from' => 'B4', 'to' => 'B7'],
['from' => 'B4', 'to' => 'B10'],
['from' => 'D2', 'to' => 'D5'],
['from' => 'D2', 'to' => 'D8'],
['from' => 'D3', 'to' => 'D6'],
['from' => 'D3', 'to' => 'D9'],
['from' => 'F2', 'to' => 'F5'],
['from' => 'F2', 'to' => 'F8'],
['from' => 'F3', 'to' => 'F6'],
['from' => 'F3', 'to' => 'F9'],
],
'merge_cells' => [
'D1:E1', 'F1:G1',
'D2:E2', 'F2:G2',
'A5:A7', 'B5:C5', 'D5:E5', 'F5:G5',
'A8:A10', 'B8:C8', 'D8:E8', 'F8:G8',
'D3:E3', 'F3:G3',
'B6:C6', 'D6:E6', 'F6:G6',
'B9:C9', 'D9:E9', 'F9:G9',
'B7:C7', 'B10:C10',
],
'copy_width' => [
['from' => 'B', 'to' => 'D'],
['from' => 'B', 'to' => 'F'],
['from' => 'C', 'to' => 'E'],
['from' => 'C', 'to' => 'G'],
],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_list_long()
{
$data = [
[
'values' => [
1 => [
'A' => '[foo1]',
'B' => '[bar1]',
'C' => 'foo1',
'D' => 'bar1',
],
10 => [
'A' => '[foo2]',
'B' => '[bar2]',
'C' => 'foo2',
'D' => 'bar2',
],
],
'data' => [
'foo1' => [
'foo1-1', 'foo1-2', 'foo1-3', 'foo1-4', 'foo1-5', 'foo1-6', 'foo1-7', 'foo1-8',
'foo1-9', 'foo1-10', 'foo1-11', 'foo1-12', 'foo1-13', 'foo1-14', 'foo1-15', 'foo1-16',
],
'bar1' => [
'bar1-1', 'bar1-2', 'bar1-3', 'bar1-4', 'bar1-5', 'bar1-6', 'bar1-7', 'bar1-8',
'bar1-9', 'bar1-10', 'bar1-11', 'bar1-12', 'bar1-13', 'bar1-14', 'bar1-15', 'bar1-16',
],
'foo2' => [
'foo2-1', 'foo2-2', 'foo2-3', 'foo2-4', 'foo2-5', 'foo2-6', 'foo2-7', 'foo2-8',
'foo2-9', 'foo2-10', 'foo2-11', 'foo2-12', 'foo2-13', 'foo2-14', 'foo2-15', 'foo2-16',
],
'bar2' => [
'bar2-1', 'bar2-2', 'bar2-3', 'bar2-4', 'bar2-5', 'bar2-6', 'bar2-7', 'bar2-8',
'bar2-9', 'bar2-10', 'bar2-11', 'bar2-12', 'bar2-13', 'bar2-14', 'bar2-15', 'bar2-16',
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'foo1-1', 'B' => 'bar1-1', 'C' => 'foo1', 'D' => 'bar1'],
2 => ['A' => 'foo1-2', 'B' => 'bar1-2', 'C' => 'foo1', 'D' => 'bar1'],
3 => ['A' => 'foo1-3', 'B' => 'bar1-3', 'C' => 'foo1', 'D' => 'bar1'],
4 => ['A' => 'foo1-4', 'B' => 'bar1-4', 'C' => 'foo1', 'D' => 'bar1'],
5 => ['A' => 'foo1-5', 'B' => 'bar1-5', 'C' => 'foo1', 'D' => 'bar1'],
6 => ['A' => 'foo1-6', 'B' => 'bar1-6', 'C' => 'foo1', 'D' => 'bar1'],
7 => ['A' => 'foo1-7', 'B' => 'bar1-7', 'C' => 'foo1', 'D' => 'bar1'],
8 => ['A' => 'foo1-8', 'B' => 'bar1-8', 'C' => 'foo1', 'D' => 'bar1'],
9 => ['A' => 'foo1-9', 'B' => 'bar1-9', 'C' => 'foo1', 'D' => 'bar1'],
10 => ['A' => 'foo1-10', 'B' => 'bar1-10', 'C' => 'foo1', 'D' => 'bar1'],
11 => ['A' => 'foo1-11', 'B' => 'bar1-11', 'C' => 'foo1', 'D' => 'bar1'],
12 => ['A' => 'foo1-12', 'B' => 'bar1-12', 'C' => 'foo1', 'D' => 'bar1'],
13 => ['A' => 'foo1-13', 'B' => 'bar1-13', 'C' => 'foo1', 'D' => 'bar1'],
14 => ['A' => 'foo1-14', 'B' => 'bar1-14', 'C' => 'foo1', 'D' => 'bar1'],
15 => ['A' => 'foo1-15', 'B' => 'bar1-15', 'C' => 'foo1', 'D' => 'bar1'],
16 => ['A' => 'foo1-16', 'B' => 'bar1-16', 'C' => 'foo1', 'D' => 'bar1'],
25 => ['A' => 'foo2-1', 'B' => 'bar2-1', 'C' => 'foo2', 'D' => 'bar2'],
26 => ['A' => 'foo2-2', 'B' => 'bar2-2', 'C' => 'foo2', 'D' => 'bar2'],
27 => ['A' => 'foo2-3', 'B' => 'bar2-3', 'C' => 'foo2', 'D' => 'bar2'],
28 => ['A' => 'foo2-4', 'B' => 'bar2-4', 'C' => 'foo2', 'D' => 'bar2'],
29 => ['A' => 'foo2-5', 'B' => 'bar2-5', 'C' => 'foo2', 'D' => 'bar2'],
30 => ['A' => 'foo2-6', 'B' => 'bar2-6', 'C' => 'foo2', 'D' => 'bar2'],
31 => ['A' => 'foo2-7', 'B' => 'bar2-7', 'C' => 'foo2', 'D' => 'bar2'],
32 => ['A' => 'foo2-8', 'B' => 'bar2-8', 'C' => 'foo2', 'D' => 'bar2'],
33 => ['A' => 'foo2-9', 'B' => 'bar2-9', 'C' => 'foo2', 'D' => 'bar2'],
34 => ['A' => 'foo2-10', 'B' => 'bar2-10', 'C' => 'foo2', 'D' => 'bar2'],
35 => ['A' => 'foo2-11', 'B' => 'bar2-11', 'C' => 'foo2', 'D' => 'bar2'],
36 => ['A' => 'foo2-12', 'B' => 'bar2-12', 'C' => 'foo2', 'D' => 'bar2'],
37 => ['A' => 'foo2-13', 'B' => 'bar2-13', 'C' => 'foo2', 'D' => 'bar2'],
38 => ['A' => 'foo2-14', 'B' => 'bar2-14', 'C' => 'foo2', 'D' => 'bar2'],
39 => ['A' => 'foo2-15', 'B' => 'bar2-15', 'C' => 'foo2', 'D' => 'bar2'],
40 => ['A' => 'foo2-16', 'B' => 'bar2-16', 'C' => 'foo2', 'D' => 'bar2'],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 15],
['action' => 'add', 'row' => 26, 'qty' => 15],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A16'],
['from' => 'A25', 'to' => 'A26:A40'],
['from' => 'B1', 'to' => 'B2:B16'],
['from' => 'B25', 'to' => 'B26:B40'],
['from' => 'C1', 'to' => 'C2:C16'],
['from' => 'C25', 'to' => 'C26:C40'],
['from' => 'D1', 'to' => 'D2:D16'],
['from' => 'D25', 'to' => 'D26:D40'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_several_tables1()
{
$data = [
[
'values' => [
1 => [
'A' => '[tableOne.a]',
'C' => '',
'D' => '[tableOne.d]',
],
3 => [
'A' => '[tableTwo.a]',
'C' => '',
'D' => '[tableTwo.d]',
],
5 => [
'A' => '[tableThree.a]',
'C' => '',
'D' => '[tableThree.d]',
],
],
'data' => [
'tableOne' => [
'a' => ['one-a-1', 'one-a-2', 'one-a-3'],
'd' => ['one-d-1', 'one-d-2', 'one-d-3'],
],
'tableTwo' => [
'a' => ['two-a-1', 'two-a-2', 'two-a-3'],
'd' => ['two-d-1', 'two-d-2', 'two-d-3'],
],
'tableThree' => [
'a' => ['three-a-1', 'three-a-2', 'three-a-3'],
'd' => ['three-d-1', 'three-d-2', 'three-d-3'],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'one-a-1', 'D' => 'one-d-1'],
2 => ['A' => 'one-a-2', 'D' => 'one-d-2'],
3 => ['A' => 'one-a-3', 'D' => 'one-d-3'],
5 => ['A' => 'two-a-1', 'D' => 'two-d-1'],
6 => ['A' => 'two-a-2', 'D' => 'two-d-2'],
7 => ['A' => 'two-a-3', 'D' => 'two-d-3'],
9 => ['A' => 'three-a-1', 'D' => 'three-d-1'],
10 => ['A' => 'three-a-2', 'D' => 'three-d-2'],
11 => ['A' => 'three-a-3', 'D' => 'three-d-3'],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
['action' => 'add', 'row' => 6, 'qty' => 2],
['action' => 'add', 'row' => 10, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'A5', 'to' => 'A6:A7'],
['from' => 'A9', 'to' => 'A10:A11'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'B5', 'to' => 'B6:B7'],
['from' => 'B9', 'to' => 'B10:B11'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'C5', 'to' => 'C6:C7'],
['from' => 'C9', 'to' => 'C10:C11'],
['from' => 'D1', 'to' => 'D2:D3'],
['from' => 'D5', 'to' => 'D6:D7'],
['from' => 'D9', 'to' => 'D10:D11'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_several_tables2()
{
$data = [
[
'values' => [
1 => [
'A' => '[tableOne.a]',
'C' => null,
'D' => '[tableOne.d]',
],
3 => [
'A' => '[tableTwo.a]',
'C' => null,
'D' => '[tableTwo.d]',
],
5 => [
'A' => '[tableThree.a]',
'C' => null,
'D' => '[tableThree.d]',
],
],
'data' => [
'tableOne' => [
'a' => ['one-a-1'],
'd' => ['one-d-1'],
],
'tableTwo' => [],
'tableThree' => [
'a' => ['three-a-1', 'three-a-2', 'three-a-3'],
'd' => ['three-d-1', 'three-d-2', 'three-d-3'],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'one-a-1', 'D' => 'one-d-1'],
3 => ['A' => null, 'D' => null],
5 => ['A' => 'three-a-1', 'D' => 'three-d-1'],
6 => ['A' => 'three-a-2', 'D' => 'three-d-2'],
7 => ['A' => 'three-a-3', 'D' => 'three-d-3'],
],
'rows' => [
['action' => 'add', 'row' => 6, 'qty' => 2],
],
'copy_style' => [
['from' => 'A5', 'to' => 'A6:A7'],
['from' => 'B5', 'to' => 'B6:B7'],
['from' => 'C5', 'to' => 'C6:C7'],
['from' => 'D5', 'to' => 'D6:D7'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_several_tables3()
{
$data = [
[
'values' => [
1 => [
'A' => '[tableOne.a]',
'B' => '[tableOne.b]',
],
3 => [
'A' => '[tableTwo.a]',
'B' => '[tableTwo.b]',
],
5 => [
'A' => '[tableThree.a]',
'B' => '[tableThree.b]',
],
],
'data' => [
'tableOne' => [
'a' => ['one-a-1', 'one-a-2', 'one-a-3'],
'b' => ['one-b-1', 'one-b-2', 'one-b-3'],
],
'tableTwo' => [
'a' => ['two-a-1', 'two-a-2', 'two-a-3'],
'b' => ['two-b-1', 'two-b-2', 'two-b-3'],
],
'tableThree' => [
'a' => ['three-a-1', 'three-a-2', 'three-a-3'],
'b' => ['three-b-1', 'three-b-2', 'three-b-3'],
],
],
'merge_cells' => ['A1:A2', 'B1:B2', 'A3:A4', 'B3:B4', 'A5:A6', 'B5:B6'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'one-a-1', 'B' => 'one-b-1'],
3 => ['A' => 'one-a-2', 'B' => 'one-b-2'],
5 => ['A' => 'one-a-3', 'B' => 'one-b-3'],
7 => ['A' => 'two-a-1', 'B' => 'two-b-1'],
9 => ['A' => 'two-a-2', 'B' => 'two-b-2'],
11 => ['A' => 'two-a-3', 'B' => 'two-b-3'],
13 => ['A' => 'three-a-1', 'B' => 'three-b-1'],
15 => ['A' => 'three-a-2', 'B' => 'three-b-2'],
17 => ['A' => 'three-a-3', 'B' => 'three-b-3'],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 4],
['action' => 'add', 'row' => 9, 'qty' => 4],
['action' => 'add', 'row' => 15, 'qty' => 4],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A3'],
['from' => 'A1', 'to' => 'A5'],
['from' => 'A7', 'to' => 'A9'],
['from' => 'A7', 'to' => 'A11'],
['from' => 'A13', 'to' => 'A15'],
['from' => 'A13', 'to' => 'A17'],
['from' => 'B1', 'to' => 'B3'],
['from' => 'B1', 'to' => 'B5'],
['from' => 'B7', 'to' => 'B9'],
['from' => 'B7', 'to' => 'B11'],
['from' => 'B13', 'to' => 'B15'],
['from' => 'B13', 'to' => 'B17'],
],
'merge_cells' => [
'A3:A4', 'B3:B4',
'A5:A6', 'B5:B6',
'A9:A10', 'B9:B10',
'A11:A12', 'B11:B12',
'A15:A16', 'B15:B16',
'A17:A18', 'B17:B18',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_several_tables4()
{
$data = [
[
'values' => [
1 => [
'A' => '[tableOne.a] [= tableOne]',
'B' => '[tableOne.b]',
],
3 => [
'A' => '[tableTwo.a] [= tableTwo]',
'B' => '[tableTwo.b]',
],
4 => [
'A' => '[tableThree.a] [= tableThree]',
'B' => '[tableThree.b]',
],
],
'data' => [
'tableOne' => [
'a' => ['one-a-1', 'one-a-2', 'one-a-3'],
'b' => ['one-b-1', 'one-b-2', 'one-b-3'],
],
'tableTwo' => [],
'tableThree' => [
'a' => ['three-a-1', 'three-a-2', 'three-a-3'],
'b' => ['three-b-1', 'three-b-2', 'three-b-3'],
],
],
'merge_cells' => ['A1:A2', 'B1:B2', 'A4:A5', 'B4:B5'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'one-a-1', 'B' => 'one-b-1'],
3 => ['A' => 'one-a-2', 'B' => 'one-b-2'],
5 => ['A' => 'one-a-3', 'B' => 'one-b-3'],
7 => ['A' => 'three-a-1', 'B' => 'three-b-1'],
9 => ['A' => 'three-a-2', 'B' => 'three-b-2'],
11 => ['A' => 'three-a-3', 'B' => 'three-b-3'],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 4],
['action' => 'delete', 'row' => 7, 'qty' => 1],
['action' => 'add', 'row' => 9, 'qty' => 4],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A3'],
['from' => 'A1', 'to' => 'A5'],
['from' => 'A7', 'to' => 'A9'],
['from' => 'A7', 'to' => 'A11'],
['from' => 'B1', 'to' => 'B3'],
['from' => 'B1', 'to' => 'B5'],
['from' => 'B7', 'to' => 'B9'],
['from' => 'B7', 'to' => 'B11'],
],
'merge_cells' => [
'A3:A4', 'B3:B4',
'A5:A6', 'B5:B6',
'A9:A10', 'B9:B10',
'A11:A12', 'B11:B12',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_merge1()
{
$data = [
[
'values' => [
1 => [
'A' => '[table.a] / [table.b] [$= table.a]',
'B' => 'foo',
'D' => 'bar',
],
2 => [
'A' => 'baz',
'D' => null,
],
],
'data' => [
'table' => [
'a' => ['one-a-1', null, 'one-a-3'],
'b' => ['one-b-1', 'one-b-2', 'one-b-3'],
],
],
'merge_cells' => ['B1:C1', 'D1:F1'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'one-a-1 / one-b-1', 'B' => 'foo', 'D' => 'bar'],
2 => ['A' => null, 'B' => 'foo', 'D' => 'bar'],
3 => ['A' => 'one-a-3 / one-b-3', 'B' => 'foo', 'D' => 'bar'],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'D1', 'to' => 'D2:D3'],
],
'merge_cells' => [
'B2:C2', 'D2:F2',
'B3:C3', 'D3:F3',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_merge2()
{
$data = [
[
'values' => [
1 => [
'A' => 'Hello',
'B' => '[table.a]',
'C' => 'bar 1',
'D' => 'bar 2',
'F' => 'bar 3',
],
],
'data' => [
'table' => [
'a' => ['one', 'two', 'three'],
],
],
'merge_cells' => ['A1:A3', 'D1:E1', 'F1:H1'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'Hello', 'B' => 'one', 'C' => 'bar 1', 'D' => 'bar 2', 'F' => 'bar 3'],
2 => ['B' => 'two', 'C' => 'bar 1', 'D' => 'bar 2', 'F' => 'bar 3'],
3 => ['B' => 'three', 'C' => 'bar 1', 'D' => 'bar 2', 'F' => 'bar 3'],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'D1', 'to' => 'D2:D3'],
['from' => 'F1', 'to' => 'F2:F3'],
],
'merge_cells' => [
'D2:E2', 'F2:H2',
'D3:E3', 'F3:H3',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_merge3()
{
$data = [
[
'values' => [
1 => [
'A' => 'Hello',
'C' => '[table.a]',
'D' => 0,
'E' => '0',
'F' => null,
'G' => '',
'H' => '=ROW()-5',
],
2 => [
'A' => '[foo]',
'B' => '[bar]',
'C' => 1,
'D' => '1',
'H' => null,
],
],
'data' => [
'table' => [
'a' => ['one', 'two', 'three'],
],
'bar' => 'hello',
],
'merge_cells' => ['A1:B3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 'Hello', 'C' => 'one', 'D' => 0, 'E' => '0', 'H' => '=ROW()-5'],
2 => ['C' => 'two', 'D' => 0, 'E' => '0', 'H' => '=ROW()-5'],
3 => ['C' => 'three', 'D' => 0, 'E' => '0', 'H' => '=ROW()-5'],
4 => ['A' => null, 'B' => 'hello'],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'D1', 'to' => 'D2:D3'],
['from' => 'E1', 'to' => 'E2:E3'],
['from' => 'F1', 'to' => 'F2:F3'],
['from' => 'G1', 'to' => 'G2:G3'],
['from' => 'H1', 'to' => 'H2:H3'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_merge4()
{
$data = [
[
'values' => [
1 => [
'A' => 'Hello',
'B' => null,
],
2 => [
'B' => '[table.a]',
],
4 => [
'A' => '[bar]',
'B' => null,
],
],
'data' => [
'table' => [
'a' => ['one', 'two', 'three'],
],
'bar' => 'hello',
],
'merge_cells' => ['A1:B3'],
],
[
'values' => [
1 => [
'A' => null,
],
2 => [
'B' => '[table.a]',
],
4 => [
'A' => '[bar]'
],
],
'data' => [
'table' => [
'a' => ['one', 'two', 'three'],
],
'bar' => 'hello',
],
'merge_cells' => ['A1:B3'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
2 => ['B' => 'one'],
3 => ['B' => 'two'],
4 => ['B' => 'three'],
6 => ['A' => 'hello'],
],
'rows' => [
['action' => 'add', 'row' => 3, 'qty' => 2],
],
'copy_style' => [
['from' => 'B2', 'to' => 'B3:B4'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_merge5()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(B3:B3)',
'B' => null,
],
2 => [
'A' => 'Foo',
'B' => null,
],
3 => [
'B' => '[product.amount]'
],
],
'data' => [
'product' => [
'amount' => [111, 222, 333],
],
],
'merge_cells' => ['A2:A4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => '=SUM(B3:B5)'],
3 => ['B' => 111],
4 => ['B' => 222],
5 => ['B' => 333],
],
'rows' => [
['action' => 'add', 'row' => 4, 'qty' => 2],
],
'copy_style' => [
['from' => 'B3', 'to' => 'B4:B5'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_merge6()
{
$data = [
[
'values' => [
1 => [
'A' => '=SUM(B3:B3)',
'B' => null,
],
2 => [
'A' => '[product.id]',
'B' => null,
],
3 => [
'B' => '[product.amount]'
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
'amount' => [111, 222, 333],
],
],
'merge_cells' => ['A2:A4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => '=SUM(B3:B3)'],
2 => ['A' => 1],
3 => ['B' => 111],
5 => ['A' => 2],
6 => ['B' => 222],
8 => ['A' => 3],
9 => ['B' => 333],
],
'rows' => [
['action' => 'add', 'row' => 5, 'qty' => 6],
],
'copy_style' => [
['from' => 'A2', 'to' => 'A5'],
['from' => 'A2', 'to' => 'A8'],
['from' => 'B2', 'to' => 'B5'],
['from' => 'B2', 'to' => 'B8'],
['from' => 'B3', 'to' => 'B6'],
['from' => 'B3', 'to' => 'B9'],
],
'merge_cells' => ['A5:A7', 'A8:A10'],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_alias1()
{
$data = [
[
'values' => [
1 => [
'A' => '[product.id]',
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => [],
],
[
'values' => [
1 => [
'A' => '[product.id.*]',
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => [],
],
[
'values' => [
1 => [
'A' => '[product.id]',
],
],
'data' => [
'product' => [
['id' => 1],
['id' => 2],
['id' => 3],
],
],
'merge_cells' => [],
],
[
'values' => [
1 => [
'A' => '[product.*.id]',
],
],
'data' => [
'product' => [
['id' => 1],
['id' => 2],
['id' => 3],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 1],
2 => ['A' => 2],
3 => ['A' => 3],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_alias2()
{
$data = [
[
'values' => [
1 => [
'A' => '[product.*.id]',
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => [],
],
[
'values' => [
1 => [
'A' => '[product.id.*]',
],
],
'data' => [
'product' => [
['id' => 1],
['id' => 2],
['id' => 3],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => null],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_alias3()
{
$data = [
[
'values' => [
1 => [
'A' => '[product.id.2]',
],
2 => [
'A' => '[product.id.1]',
],
3 => [
'A' => '[product.id.0]',
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => [],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 3],
2 => ['A' => 2],
3 => ['A' => 1],
],
'rows' => [],
'copy_style' => [],
'merge_cells' => [],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_styles1()
{
$data = [
[
'values' => [
1 => [
'A' => '[product.id]',
'E' => null,
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => ['C1:D1'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 1],
2 => ['A' => 2],
3 => ['A' => 3],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'E1', 'to' => 'E2:E3'],
],
'merge_cells' => [
'C2:D2', 'C3:D3',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_styles2()
{
$data = [
[
'values' => [
1 => [
'A' => '[product.id]',
'D' => null,
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => ['C1:D1'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 1],
2 => ['A' => 2],
3 => ['A' => 3],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'C1', 'to' => 'C2:C3'],
],
'merge_cells' => [
'C2:D2', 'C3:D3',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_styles3()
{
$data = [
[
'values' => [
1 => [
'B' => '[product.id]',
'E' => null,
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => ['C1:D1'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['B' => 1],
2 => ['B' => 2],
3 => ['B' => 3],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'E1', 'to' => 'E2:E3'],
],
'merge_cells' => [
'C2:D2', 'C3:D3',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_styles4()
{
$data = [
[
'values' => [
1 => [
'A' => '[product.id]',
'BA' => null,
],
],
'data' => [
'product' => [
'id' => [1, 2, 3],
],
],
'merge_cells' => ['AB1:BA1'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['A' => 1],
2 => ['A' => 2],
3 => ['A' => 3],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'AA1', 'to' => 'AA2:AA3'],
['from' => 'AB1', 'to' => 'AB2:AB3'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'D1', 'to' => 'D2:D3'],
['from' => 'E1', 'to' => 'E2:E3'],
['from' => 'F1', 'to' => 'F2:F3'],
['from' => 'G1', 'to' => 'G2:G3'],
['from' => 'H1', 'to' => 'H2:H3'],
['from' => 'I1', 'to' => 'I2:I3'],
['from' => 'J1', 'to' => 'J2:J3'],
['from' => 'K1', 'to' => 'K2:K3'],
['from' => 'L1', 'to' => 'L2:L3'],
['from' => 'M1', 'to' => 'M2:M3'],
['from' => 'N1', 'to' => 'N2:N3'],
['from' => 'O1', 'to' => 'O2:O3'],
['from' => 'P1', 'to' => 'P2:P3'],
['from' => 'Q1', 'to' => 'Q2:Q3'],
['from' => 'R1', 'to' => 'R2:R3'],
['from' => 'S1', 'to' => 'S2:S3'],
['from' => 'T1', 'to' => 'T2:T3'],
['from' => 'U1', 'to' => 'U2:U3'],
['from' => 'V1', 'to' => 'V2:V3'],
['from' => 'W1', 'to' => 'W2:W3'],
['from' => 'X1', 'to' => 'X2:X3'],
['from' => 'Y1', 'to' => 'Y2:Y3'],
['from' => 'Z1', 'to' => 'Z2:Z3'],
],
'merge_cells' => [
'AB2:BA2', 'AB3:BA3',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_styles5()
{
$data = [
[
'values' => [
1 => [
'C' => '[foo.id]',
'E' => null,
],
2 => [
'A' => '[bar.id]',
'E' => null,
],
],
'data' => [
'foo' => [
'id' => [1, 2, 3],
],
'bar' => [
'id' => [4, 5, 6],
],
],
'merge_cells' => ['C1:E1', 'A2:D2'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['C' => 1],
2 => ['C' => 2],
3 => ['C' => 3],
4 => ['A' => 4],
5 => ['A' => 5],
6 => ['A' => 6],
],
'rows' => [
['action' => 'add', 'row' => 2, 'qty' => 2],
['action' => 'add', 'row' => 5, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'A4', 'to' => 'A5:A6'],
['from' => 'B1', 'to' => 'B2:B3'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'E4', 'to' => 'E5:E6'],
],
'merge_cells' => [
'C2:E2', 'C3:E3',
'A5:D5', 'A6:D6',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
/**
* @return void
*/
public function test_schema_styles6()
{
$data = [
[
'values' => [
1 => [
'A' => '[= baz]',
'H' => null,
],
2 => [
'C' => '[foo.id]',
'H' => null,
],
4 => [
'A' => '[bar.id]',
'H' => null,
],
],
'data' => [
'foo' => [
'id' => [1, 2, 3],
],
'bar' => [
'id' => [4, 5, 6],
],
'' => 'baz',
],
'merge_cells' => ['A2:B2', 'C2:G2', 'A4:H4'],
],
];
foreach ($data as $id => $item) {
$this->assertSame(
[
'data' => [
1 => ['C' => 1],
2 => ['C' => 2],
3 => ['C' => 3],
5 => ['A' => 4],
6 => ['A' => 5],
7 => ['A' => 6],
],
'rows' => [
['action' => 'delete', 'row' => 1, 'qty' => 1],
['action' => 'add', 'row' => 2, 'qty' => 2],
['action' => 'add', 'row' => 6, 'qty' => 2],
],
'copy_style' => [
['from' => 'A1', 'to' => 'A2:A3'],
['from' => 'A5', 'to' => 'A6:A7'],
['from' => 'C1', 'to' => 'C2:C3'],
['from' => 'H1', 'to' => 'H2:H3'],
],
'merge_cells' => [
'A2:B2', 'C2:G2', 'A3:B3', 'C3:G3',
'A6:H6', 'A7:H7',
],
'copy_width' => [],
],
$this->service->schema($item['values'], $item['data'], $item['merge_cells'])->toArray(),
"$id"
);
}
}
}
================================================
FILE: tests/TraitsTest.php
================================================
assertTrue($this->isColumnLE('A', 'B'));
$this->assertTrue($this->isColumnLE('B', 'B'));
$this->assertFalse($this->isColumnLE('C', 'B'));
$this->assertTrue($this->isColumnLE('Z', 'AA'));
$this->assertTrue($this->isColumnLE('AA', 'AA'));
$this->assertFalse($this->isColumnLE('AB', 'AA'));
$this->assertFalse($this->isColumnLE('AAA', 'ZZ'));
$this->assertTrue($this->isColumnLE('AAA', 'AAA'));
$this->assertTrue($this->isColumnLE('AAA', 'AAB'));
}
/**
* @return void
*/
public function test_isColumnGE()
{
$this->assertFalse($this->isColumnGE('A', 'B'));
$this->assertTrue($this->isColumnGE('B', 'B'));
$this->assertTrue($this->isColumnGE('C', 'B'));
$this->assertFalse($this->isColumnGE('Z', 'AA'));
$this->assertTrue($this->isColumnGE('AA', 'AA'));
$this->assertTrue($this->isColumnGE('AB', 'AA'));
$this->assertTrue($this->isColumnGE('AAA', 'ZZ'));
$this->assertTrue($this->isColumnGE('AAA', 'AAA'));
$this->assertFalse($this->isColumnGE('AAA', 'AAB'));
}
/**
* @return void
*/
public function test_sort()
{
$columns = ['BA' => 7, 'AZ' => 6, 'A' => 1, 'B' => 2, 'Z' => 3, 'AA' => 4, 'AB' => 5];
uksort($columns, fn ($a, $b) => $this->isColumnLE($a, $b) ? -1 : 1);
$this->assertSame(
['A' => 1, 'B' => 2, 'Z' => 3, 'AA' => 4, 'AB' => 5, 'AZ' => 6, 'BA' => 7],
$columns
);
}
}