Skip to content

Added ability to template formulas with cell referencing #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,67 @@ There are special templates, which require to insert the whole row, and not inse

For these templates, a special setter has been created: CellSetterArrayValueSpecial. Examples of code that uses it is given in folder: samples/8_special_template.

## Formula setter

There are cases, when you need to set formulas depending on cloned rows.

For example:

![Cell Formula Example](readme_resources/cell_formula.png)

In this case formula for rows in column C, should be dynamically changed, based on values in column A and B.

To solve this case, there is `CellSetterFormulaValue` setter.

### Usage:

Template

![Cell Formula Template](readme_resources/cell_formula_template.png)

```php
<?php

require( __DIR__ . '/../Bootstrap.php');

use alhimik1986\PhpExcelTemplator\PhpExcelTemplator;
use alhimik1986\PhpExcelTemplator\params\ExcelParam;
use alhimik1986\PhpExcelTemplator\setters\CellSetterArrayValueSpecial;
use alhimik1986\PhpExcelTemplator\setters\CellSetterFormulaValue;
use alhimik1986\PhpExcelTemplator\setters\DTO\FormulaValue;

$templateFile = './template.xlsx';
$fileName = './exported_file.xlsx';
define('SPECIAL_ARRAY_TYPE', CellSetterArrayValueSpecial::class);
define('FORMULA_TYPE', CellSetterFormulaValue::class);

$params = [
'[col1]' => new ExcelParam(SPECIAL_ARRAY_TYPE, [1, 2, 3, 4]),
'[col2]' => new ExcelParam(SPECIAL_ARRAY_TYPE, [2, 3, 4, 5]),
'[col3]' => new ExcelParam(FORMULA_TYPE, new FormulaValue('=(%-2,0%)+(%-1,0%)', 4)),
];

PhpExcelTemplator::saveToFile($templateFile, $fileName, $params);
// PhpExcelTemplator::outputToFile($templateFile, $fileName, $params); // to download the file from web page
```

Result:

![Cell Formula Result](readme_resources/cell_forumla_result.png)

As you can see class `FormulaValue` receives 2 arguments:
- Formula, with reference cells (more about them later)
- Quantity to clone

In formulas, you can define neighbouring cells by reference to them.

Reference signature `(% <column reference>,<row reference> %)`

For example (we are on C2 cell):
- `(%-1,0%)` = B2
- `(%1,-2)` = D0
- `(%1,1%)` = D3
- etc

## Events

Expand Down
62 changes: 62 additions & 0 deletions README_ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,68 @@ PhpExcelTemplator::saveToFile('./template.xlsx', './exported_file.xlsx', $params

Для таких шаблонов создан специальный сеттер: CellSetterArrayValueSpecial. Пример его использования приведён в папке: samples/8_special_template.

## Сеттер для формул

Бывают случае, когда необходимо проставлять формулы в зависимости от соседних ячеек.

Например:

![Cell Formula Example](readme_resources/cell_formula.png)

В данном случае, значение формулы ячейках колонки С должны меняться в соответствии со значениями колонок A и B.

Для данного кейса есть сеттер `CellSetterFormulaValue`.

### Способ применения:

Шаблон

![Cell Formula Template](readme_resources/cell_formula_template.png)

```php
<?php

require( __DIR__ . '/../Bootstrap.php');

use alhimik1986\PhpExcelTemplator\PhpExcelTemplator;
use alhimik1986\PhpExcelTemplator\params\ExcelParam;
use alhimik1986\PhpExcelTemplator\setters\CellSetterArrayValueSpecial;
use alhimik1986\PhpExcelTemplator\setters\CellSetterFormulaValue;
use alhimik1986\PhpExcelTemplator\setters\DTO\FormulaValue;

$templateFile = './template.xlsx';
$fileName = './exported_file.xlsx';
define('SPECIAL_ARRAY_TYPE', CellSetterArrayValueSpecial::class);
define('FORMULA_TYPE', CellSetterFormulaValue::class);

$params = [
'[col1]' => new ExcelParam(SPECIAL_ARRAY_TYPE, [1, 2, 3, 4]),
'[col2]' => new ExcelParam(SPECIAL_ARRAY_TYPE, [2, 3, 4, 5]),
'[col3]' => new ExcelParam(FORMULA_TYPE, new FormulaValue('=(%-2,0%)+(%-1,0%)', 4)),
];

PhpExcelTemplator::saveToFile($templateFile, $fileName, $params);
// PhpExcelTemplator::outputToFile($templateFile, $fileName, $params); // to download the file from web page
```

Результат:

![Cell Formula Result](readme_resources/cell_forumla_result.png)

Класс `FormulaValue` принимает 2 аргумента:
- Формулу со ссылками на соседние ячейки,
- Кол-во повторений

В формулах можно указать ячейки с помощью ссылок.

Сигнатура ссылки `(% <отношение к колонке>,<отношение к строке> %)`

Например (мы находимся в ячейке C2):
- `(%-1,0%)` = B2
- `(%1,-2)` = D0
- `(%1,1%)` = D3
- итд


## Использование событий

Expand Down
Binary file added readme_resources/cell_formula.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme_resources/cell_formula_template.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme_resources/cell_forumla_result.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/11_formula/exported_file.xlsx
Binary file not shown.
23 changes: 23 additions & 0 deletions samples/11_formula/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

require( __DIR__ . '/../Bootstrap.php');

use alhimik1986\PhpExcelTemplator\PhpExcelTemplator;
use alhimik1986\PhpExcelTemplator\params\ExcelParam;
use alhimik1986\PhpExcelTemplator\setters\CellSetterArrayValueSpecial;
use alhimik1986\PhpExcelTemplator\setters\CellSetterFormulaValue;
use alhimik1986\PhpExcelTemplator\setters\DTO\FormulaValue;

$templateFile = './template.xlsx';
$fileName = './exported_file.xlsx';
define('SPECIAL_ARRAY_TYPE', CellSetterArrayValueSpecial::class);
define('FORMULA_TYPE', CellSetterFormulaValue::class);

$params = [
'[col1]' => new ExcelParam(SPECIAL_ARRAY_TYPE, [1, 2, 3, 4]),
'[col2]' => new ExcelParam(SPECIAL_ARRAY_TYPE, [2, 3, 4, 5]),
'[col3]' => new ExcelParam(FORMULA_TYPE, new FormulaValue('=(%-2,0%)+(%-1,0%)', 4)),
];

PhpExcelTemplator::saveToFile($templateFile, $fileName, $params);
// PhpExcelTemplator::outputToFile($templateFile, $fileName, $params); // to download the file from web page
1 change: 1 addition & 0 deletions samples/11_formula/run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@php index.php
Binary file added samples/11_formula/template.xlsx
Binary file not shown.
102 changes: 102 additions & 0 deletions src/setters/CellSetterFormulaValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace alhimik1986\PhpExcelTemplator\setters;

use alhimik1986\PhpExcelTemplator\InsertedCells;
use alhimik1986\PhpExcelTemplator\params\SetterParam;
use alhimik1986\PhpExcelTemplator\setters\DTO\FormulaValue;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

class CellSetterFormulaValue implements ICellSetter
{

public function setCellValue(SetterParam $setterParam, InsertedCells $insertedCells): InsertedCells
{
$sheet = $setterParam->sheet;
$rowKey = $setterParam->rowKey;
$colKey = $setterParam->colKey;
$tplVarName = $setterParam->tplVarName;
$param = $setterParam->params[$tplVarName];
if (!$this->_validateValue($param->value)) {
return $insertedCells;
}

$pColumn = $insertedCells->getCurrentCol($rowKey, $colKey);
$pColumnIndex = $insertedCells->getCurrentColIndex($rowKey, $colKey);
$pRow = $insertedCells->getCurrentRowIndex($rowKey, $colKey);

/** @var FormulaValue $value */
$value = $param->value;

$this->_insertNewRowsIfNeed($sheet, $value, $insertedCells, $rowKey, $pColumnIndex, $pRow);

$formulaValue = $value->getFormula();
$quantity = $value->getQuantity();

for($i = 0; $i <= $quantity - 1; $i++) {
$currCellCoordinates = $pColumn . ($pRow + $i);

$value = $this->modifyFormula($rowKey + $i, $colKey, $formulaValue, $insertedCells);
$sheet->setCellValue($currCellCoordinates, $value);
}

$maxInsertedRows = $this->_getMaxInsertedRows($rowKey, $insertedCells);
$maxColumnIndex = Coordinate::columnIndexFromString($sheet->getHighestColumn());
for ($i = 0; $i <= $maxColumnIndex; $i++) {
$insertedCells->setInsertedRows($rowKey, $i, $maxInsertedRows);
}

return $insertedCells;
}


private function _getMaxInsertedRows(int $rowKey, InsertedCells $insertedCells): int
{
$maxInsertedRows = 0;
foreach ($insertedCells->inserted_rows as $col_key => $insertedRowsInCol) {
$insertedRows = $insertedCells->getInsertedRows($rowKey, $col_key);
if ($insertedRows > $maxInsertedRows) {
$maxInsertedRows = $insertedRows;
}
}

return $maxInsertedRows;
}

private function _validateValue($value): bool
{
return $value instanceof FormulaValue;
}

private function _insertNewRowsIfNeed(Worksheet $sheet, FormulaValue $value, InsertedCells $insertedCells, int $row_key, int $pColumnIndex, int $pRow): void
{
$maxInsertedRows = $this->_getMaxInsertedRows($row_key, $insertedCells);
$rowsToInsert = ($value->getQuantity() - 1 > $maxInsertedRows) ? $value->getQuantity() - 1 : 0;
if ($rowsToInsert > 0) {
$sheet->insertNewRowBefore($pRow + 1, $rowsToInsert);
}
}


protected function modifyFormula($rowIndex, $columnIndex, $value, InsertedCells $insertedCells): string
{
$matches = [];
preg_match_all("/(\(%[-,+]?\d,[-,+]?\d%\))/", $value, $matches);

foreach ($matches[1] as $formula) {
$modified = str_replace(['(%', '%)'], '', $formula);
list($colModifier, $rowModifier) = explode(',', $modified);
$colModifier = (int)$colModifier;
$rowModifier = (int)$rowModifier;

$finalCol = $columnIndex + $colModifier;
$finalRow = $rowIndex + $rowModifier;
$cellCoordinate = Coordinate::stringFromColumnIndex($finalCol + 1) . (string)($finalRow+1);

$value = str_replace($formula, $cellCoordinate, $value);
}

return $value;
}
}
28 changes: 28 additions & 0 deletions src/setters/DTO/FormulaValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace alhimik1986\PhpExcelTemplator\setters\DTO;

class FormulaValue
{

protected string $formula;

protected int $quantity;

public function __construct(string $formula, int $quantity)
{
$this->formula = $formula;
$this->quantity = $quantity;
}

public function getFormula(): string
{
return $this->formula;
}

public function getQuantity(): int
{
return $this->quantity;
}

}