diff --git a/README.md b/README.md index f687fbd..7bd8044 100644 --- a/README.md +++ b/README.md @@ -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 + 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 `(% , %)` + +For example (we are on C2 cell): +- `(%-1,0%)` = B2 +- `(%1,-2)` = D0 +- `(%1,1%)` = D3 +- etc ## Events diff --git a/README_ru.md b/README_ru.md index 14862e3..ed9e15a 100644 --- a/README_ru.md +++ b/README_ru.md @@ -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 + 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 +- итд + ## Использование событий diff --git a/readme_resources/cell_formula.png b/readme_resources/cell_formula.png new file mode 100644 index 0000000..bcfbd16 Binary files /dev/null and b/readme_resources/cell_formula.png differ diff --git a/readme_resources/cell_formula_template.png b/readme_resources/cell_formula_template.png new file mode 100644 index 0000000..4b3c7a2 Binary files /dev/null and b/readme_resources/cell_formula_template.png differ diff --git a/readme_resources/cell_forumla_result.png b/readme_resources/cell_forumla_result.png new file mode 100644 index 0000000..8cfe455 Binary files /dev/null and b/readme_resources/cell_forumla_result.png differ diff --git a/samples/11_formula/exported_file.xlsx b/samples/11_formula/exported_file.xlsx new file mode 100644 index 0000000..6530c9c Binary files /dev/null and b/samples/11_formula/exported_file.xlsx differ diff --git a/samples/11_formula/index.php b/samples/11_formula/index.php new file mode 100644 index 0000000..81596ff --- /dev/null +++ b/samples/11_formula/index.php @@ -0,0 +1,23 @@ + 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 diff --git a/samples/11_formula/run.cmd b/samples/11_formula/run.cmd new file mode 100644 index 0000000..6885d00 --- /dev/null +++ b/samples/11_formula/run.cmd @@ -0,0 +1 @@ +@php index.php \ No newline at end of file diff --git a/samples/11_formula/template.xlsx b/samples/11_formula/template.xlsx new file mode 100644 index 0000000..36fc8ec Binary files /dev/null and b/samples/11_formula/template.xlsx differ diff --git a/src/setters/CellSetterFormulaValue.php b/src/setters/CellSetterFormulaValue.php new file mode 100644 index 0000000..8650e32 --- /dev/null +++ b/src/setters/CellSetterFormulaValue.php @@ -0,0 +1,102 @@ +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; + } +} \ No newline at end of file diff --git a/src/setters/DTO/FormulaValue.php b/src/setters/DTO/FormulaValue.php new file mode 100644 index 0000000..26906fa --- /dev/null +++ b/src/setters/DTO/FormulaValue.php @@ -0,0 +1,28 @@ +formula = $formula; + $this->quantity = $quantity; + } + + public function getFormula(): string + { + return $this->formula; + } + + public function getQuantity(): int + { + return $this->quantity; + } + +} \ No newline at end of file