diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f19a8cd4..1d78534a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed - Xlsx Reader Shared Formula with Boolean Result. Partial solution for [Issue #4280](https://github.com/PHPOffice/PhpSpreadsheet/issues/4280) [PR #4281](https://github.com/PHPOffice/PhpSpreadsheet/pull/4281) +- Retitling cloned Worksheets. [Issue #641](https://github.com/PHPOffice/PhpSpreadsheet/issues/641) [PR #4302](https://github.com/PHPOffice/PhpSpreadsheet/pull/4302) ## 2024-12-26 - 3.7.0 diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index 943db95cc3..f40a8889ac 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -514,7 +514,7 @@ public function getActiveSheet(): Worksheet public function createSheet(?int $sheetIndex = null): Worksheet { $newSheet = new Worksheet($this); - $this->addSheet($newSheet, $sheetIndex); + $this->addSheet($newSheet, $sheetIndex, true); return $newSheet; } @@ -535,8 +535,20 @@ public function sheetNameExists(string $worksheetName): bool * @param Worksheet $worksheet The worksheet to add * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last) */ - public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null): Worksheet + public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null, bool $retitleIfNeeded = false): Worksheet { + if ($retitleIfNeeded) { + $title = $worksheet->getTitle(); + if ($this->sheetNameExists($title)) { + $i = 1; + $newTitle = "$title $i"; + while ($this->sheetNameExists($newTitle)) { + ++$i; + $newTitle = "$title $i"; + } + $worksheet->setTitle($newTitle); + } + } if ($this->sheetNameExists($worksheet->getTitle())) { throw new Exception( "Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename this worksheet first." diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 9f9457f644..cb6b451cd4 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -321,6 +321,7 @@ public function __construct(?Spreadsheet $parent = null, string $title = 'Worksh { // Set parent and title $this->parent = $parent; + $this->hash = spl_object_id($this); $this->setTitle($title, false); // setTitle can change $pTitle $this->setCodeName($this->getTitle()); @@ -349,7 +350,6 @@ public function __construct(?Spreadsheet $parent = null, string $title = 'Worksh $this->autoFilter = new AutoFilter('', $this); // Table collection $this->tableCollection = new ArrayObject(); - $this->hash = spl_object_id($this); } /** @@ -869,7 +869,7 @@ public function setTitle(string $title, bool $updateFormulaCellReferences = true // Syntax check self::checkSheetTitle($title); - if ($this->parent) { + if ($this->parent && $this->parent->getIndex($this, true) >= 0) { // Is there already such sheet name? if ($this->parent->sheetNameExists($title)) { // Use name, but append with lowest possible integer @@ -899,7 +899,7 @@ public function setTitle(string $title, bool $updateFormulaCellReferences = true // Set title $this->title = $title; - if ($this->parent && $this->parent->getCalculationEngine()) { + if ($this->parent && $this->parent->getIndex($this, true) >= 0 && $this->parent->getCalculationEngine()) { // New title $newTitle = $this->getTitle(); $this->parent->getCalculationEngine() diff --git a/tests/PhpSpreadsheetTests/Worksheet/Issue641Test.php b/tests/PhpSpreadsheetTests/Worksheet/Issue641Test.php new file mode 100644 index 0000000000..bbb6c8bf00 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/Issue641Test.php @@ -0,0 +1,82 @@ +removeSheetByIndex(0); + $availableWs = []; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Condensed A'); + $worksheet->getCell('A1')->setValue("=SUM('Detailed A'!A1:A10)"); + $worksheet->getCell('A2')->setValue(mt_rand(1, 30)); + $availableWs[] = 'Condensed A'; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Condensed B'); + $worksheet->getCell('A1')->setValue("=SUM('Detailed B'!A1:A10)"); + $worksheet->getCell('A2')->setValue(mt_rand(1, 30)); + $availableWs[] = 'Condensed B'; + + // at this point the value in worksheet 'Condensed B' cell A1 is + // =SUM('Detailed B'!A1:A10) + + // worksheet in question is cloned and totals are attached + $totalWs1 = clone $xlsx->getSheet($xlsx->getSheetCount() - 1); + $totalWs1->setTitle('Condensed Total'); + $xlsx->addSheet($totalWs1); + $formula = '='; + foreach ($availableWs as $ws) { + $formula .= sprintf("+'%s'!A2", $ws); + } + $totalWs1->getCell('A1')->setValue("=SUM('Detailed Total'!A1:A10)"); + $totalWs1->getCell('A2')->setValue($formula); + + $availableWs = []; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Detailed A'); + for ($step = 1; $step <= 10; ++$step) { + $worksheet->getCell("A{$step}")->setValue(mt_rand(1, 30)); + } + $availableWs[] = 'Detailed A'; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Detailed B'); + for ($step = 1; $step <= 10; ++$step) { + $worksheet->getCell("A{$step}")->setValue(mt_rand(1, 30)); + } + $availableWs[] = 'Detailed B'; + + $totalWs2 = clone $xlsx->getSheet($xlsx->getSheetCount() - 1); + $totalWs2->setTitle('Detailed Total'); + $xlsx->addSheet($totalWs2); + + for ($step = 1; $step <= 10; ++$step) { + $formula = '='; + foreach ($availableWs as $ws) { + $formula .= sprintf("+'%s'!A%s", $ws, $step); + } + $totalWs2->getCell("A{$step}")->setValue($formula); + } + + self::assertSame("=SUM('Detailed A'!A1:A10)", $xlsx->getSheetByName('Condensed A')?->getCell('A1')?->getValue()); + self::assertSame("=SUM('Detailed B'!A1:A10)", $xlsx->getSheetByName('Condensed B')?->getCell('A1')?->getValue()); + self::assertSame("=SUM('Detailed Total'!A1:A10)", $xlsx->getSheetByName('Condensed Total')?->getCell('A1')?->getValue()); + self::assertSame("=+'Detailed A'!A1+'Detailed B'!A1", $xlsx->getSheetByName('Detailed Total')?->getCell('A1')?->getValue()); + + $xlsx->disconnectWorksheets(); + } +}