Merge pull request #3066 from PHPOffice/Issue-3065_Merge-Cell-Options
Additional options for mergeCells() method
This commit is contained in:
commit
a846a93f52
|
|
@ -17,7 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Nothing
|
- Allow variant behaviour when merging cells [Issue #3065](https://github.com/PHPOffice/PhpSpreadsheet/issues/3065)
|
||||||
|
- Merge methods now allow an additional `$behaviour` argument. Permitted values are:
|
||||||
|
- Worksheet::MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells (the default behaviour)
|
||||||
|
- Worksheet::MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
|
||||||
|
- Worksheet::MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -1332,22 +1332,72 @@ rows (default), or above. The following code adds the summary above:
|
||||||
$spreadsheet->getActiveSheet()->setShowSummaryBelow(false);
|
$spreadsheet->getActiveSheet()->setShowSummaryBelow(false);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Merge/unmerge cells
|
## Merge/Unmerge cells
|
||||||
|
|
||||||
If you have a big piece of data you want to display in a worksheet, you
|
If you have a big piece of data you want to display in a worksheet, or a
|
||||||
can merge two or more cells together, to become one cell. This can be
|
heading that needs to span multiple sub-heading columns, you can merge
|
||||||
done using the following code:
|
two or more cells together, to become one cell. This can be done using
|
||||||
|
the following code:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$spreadsheet->getActiveSheet()->mergeCells('A18:E22');
|
$spreadsheet->getActiveSheet()->mergeCells('A18:E22');
|
||||||
```
|
```
|
||||||
|
|
||||||
Removing a merge can be done using the unmergeCells method:
|
Removing a merge can be done using the `unmergeCells()` method:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$spreadsheet->getActiveSheet()->unmergeCells('A18:E22');
|
$spreadsheet->getActiveSheet()->unmergeCells('A18:E22');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
MS Excel itself doesn't yet offer the functionality to simply hide the merged cells, or to merge the content of cells into a single cell, but it is available in Open/Libre Office.
|
||||||
|
|
||||||
|
### Merge with MERGE_CELL_CONTENT_EMPTY
|
||||||
|
|
||||||
|
The default behaviour is to empty all cells except for the top-left corner cell in the merge range; and this is also the default behaviour for the `mergeCells()` method in PhpSpreadsheet.
|
||||||
|
When this behaviour is applied, those cell values will be set to null; and if they are subsequently Unmerged, they will be empty cells.
|
||||||
|
|
||||||
|
Passing an extra flag value to the `mergeCells()` method in PhpSpreadsheet can change this behaviour.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Possible flag values are:
|
||||||
|
- Worksheet::MERGE_CELL_CONTENT_EMPTY (the default)
|
||||||
|
- Worksheet::MERGE_CELL_CONTENT_HIDE
|
||||||
|
- Worksheet::MERGE_CELL_CONTENT_MERGE
|
||||||
|
|
||||||
|
### Merge with MERGE_CELL_CONTENT_HIDE
|
||||||
|
|
||||||
|
The first alternative, available only in OpenOffice, is to hide those cells, but to leave their content intact.
|
||||||
|
When a file saved as `Xlsx` in those applications is opened in MS Excel, and those cells are unmerged, the original content will still be present.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A1:C3', Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
|
```
|
||||||
|
|
||||||
|
Will replicate that behaviour.
|
||||||
|
|
||||||
|
### Merge with MERGE_CELL_CONTENT_MERGE
|
||||||
|
|
||||||
|
The second alternative, available in both OpenOffice and LibreOffice is to merge the content of every cell in the merge range into the top-left cell, while setting those hidden cells to empty.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$spreadsheet->getActiveSheet()->mergeCells('A1:C3', Worksheet::MERGE_CELL_CONTENT_MERGE);
|
||||||
|
```
|
||||||
|
|
||||||
|
Particularly when the merged cells contain formulae, the logic for this merge seems strange:
|
||||||
|
walking through the merge range, each cell is calculated in turn, and appended to the "master" cell, then it is emptied, so any subsequent calculations that reference the cell see an empty cell, not the pre-merge value.
|
||||||
|
For example, suppose our spreadsheet contains
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
where `B2` is the formula `=5-B1` and `C2` is the formula `=A2/B2`,
|
||||||
|
and we want to merge cells `A2` to `C2` with all the cell values merged.
|
||||||
|
The result is:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The cell value `12` from cell `A2` is fixed; the value from `B2` is the result of the formula `=5-B1` (`4`, which is appended to our merged value), and cell `B2` is then emptied, so when we evaluate cell `C2` with the formula `=A2/B2` it gives us `12 / 0` which results in a `#DIV/0!` error (so the error `#DIV/0!` is appended to our merged value rather than the original calculation result of `3`).
|
||||||
|
|
||||||
## Inserting or Removing rows/columns
|
## Inserting or Removing rows/columns
|
||||||
|
|
||||||
You can insert/remove rows/columns at a specific position. The following
|
You can insert/remove rows/columns at a specific position. The following
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ class Gnumeric extends BaseReader
|
||||||
if ($sheet !== null && isset($sheet->MergedRegions)) {
|
if ($sheet !== null && isset($sheet->MergedRegions)) {
|
||||||
foreach ($sheet->MergedRegions->Merge as $mergeCells) {
|
foreach ($sheet->MergedRegions->Merge as $mergeCells) {
|
||||||
if (strpos((string) $mergeCells, ':') !== false) {
|
if (strpos((string) $mergeCells, ':') !== false) {
|
||||||
$this->spreadsheet->getActiveSheet()->mergeCells($mergeCells);
|
$this->spreadsheet->getActiveSheet()->mergeCells($mergeCells, Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use XMLReader;
|
use XMLReader;
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
|
|
@ -759,7 +760,7 @@ class Ods extends BaseReader
|
||||||
}
|
}
|
||||||
|
|
||||||
$cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
|
$cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
|
||||||
$spreadsheet->getActiveSheet()->mergeCells($cellRange);
|
$spreadsheet->getActiveSheet()->mergeCells($cellRange, Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4585,7 +4585,7 @@ class Xls extends BaseReader
|
||||||
(strpos($cellRangeAddress, ':') !== false) &&
|
(strpos($cellRangeAddress, ':') !== false) &&
|
||||||
($this->includeCellRangeFiltered($cellRangeAddress))
|
($this->includeCellRangeFiltered($cellRangeAddress))
|
||||||
) {
|
) {
|
||||||
$this->phpSheet->mergeCells($cellRangeAddress);
|
$this->phpSheet->mergeCells($cellRangeAddress, Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -914,7 +914,7 @@ class Xlsx extends BaseReader
|
||||||
foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
|
foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
|
||||||
$mergeRef = (string) $mergeCell['ref'];
|
$mergeRef = (string) $mergeCell['ref'];
|
||||||
if (strpos($mergeRef, ':') !== false) {
|
if (strpos($mergeRef, ':') !== false) {
|
||||||
$docSheet->mergeCells((string) $mergeCell['ref']);
|
$docSheet->mergeCells((string) $mergeCell['ref'], Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
use SimpleXMLElement;
|
use SimpleXMLElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -364,7 +365,7 @@ class Xml extends BaseReader
|
||||||
$rowTo = $rowTo + $cell_ss['MergeDown'];
|
$rowTo = $rowTo + $cell_ss['MergeDown'];
|
||||||
}
|
}
|
||||||
$cellRange .= ':' . $columnTo . $rowTo;
|
$cellRange .= ':' . $columnTo . $rowTo;
|
||||||
$spreadsheet->getActiveSheet()->mergeCells($cellRange);
|
$spreadsheet->getActiveSheet()->mergeCells($cellRange, Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
$hasCalculatedValue = false;
|
$hasCalculatedValue = false;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ class Worksheet implements IComparable
|
||||||
public const SHEETSTATE_HIDDEN = 'hidden';
|
public const SHEETSTATE_HIDDEN = 'hidden';
|
||||||
public const SHEETSTATE_VERYHIDDEN = 'veryHidden';
|
public const SHEETSTATE_VERYHIDDEN = 'veryHidden';
|
||||||
|
|
||||||
|
public const MERGE_CELL_CONTENT_EMPTY = 'empty';
|
||||||
|
public const MERGE_CELL_CONTENT_HIDE = 'hide';
|
||||||
|
public const MERGE_CELL_CONTENT_MERGE = 'merge';
|
||||||
|
|
||||||
protected const SHEET_NAME_REQUIRES_NO_QUOTES = '/^[_\p{L}][_\p{L}\p{N}]*$/mui';
|
protected const SHEET_NAME_REQUIRES_NO_QUOTES = '/^[_\p{L}][_\p{L}\p{N}]*$/mui';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1758,10 +1762,15 @@ class Worksheet implements IComparable
|
||||||
* @param AddressRange|array<int>|string $range A simple string containing a Cell range like 'A1:E10'
|
* @param AddressRange|array<int>|string $range A simple string containing a Cell range like 'A1:E10'
|
||||||
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
|
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
|
||||||
* or an AddressRange.
|
* or an AddressRange.
|
||||||
|
* @param string $behaviour How the merged cells should behave.
|
||||||
|
* Possible values are:
|
||||||
|
* MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells
|
||||||
|
* MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
|
||||||
|
* MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function mergeCells($range)
|
public function mergeCells($range, $behaviour = self::MERGE_CELL_CONTENT_EMPTY)
|
||||||
{
|
{
|
||||||
$range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));
|
$range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));
|
||||||
|
|
||||||
|
|
@ -1793,18 +1802,22 @@ class Worksheet implements IComparable
|
||||||
$this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
|
$this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blank out the rest of the cells in the range (if they exist)
|
if ($behaviour !== self::MERGE_CELL_CONTENT_HIDE) {
|
||||||
if ($numberRows > $numberColumns) {
|
// Blank out the rest of the cells in the range (if they exist)
|
||||||
$this->clearMergeCellsByColumn($firstColumn, $lastColumn, $firstRow, $lastRow, $upperLeft);
|
if ($numberRows > $numberColumns) {
|
||||||
} else {
|
$this->clearMergeCellsByColumn($firstColumn, $lastColumn, $firstRow, $lastRow, $upperLeft, $behaviour);
|
||||||
$this->clearMergeCellsByRow($firstColumn, $lastColumnIndex, $firstRow, $lastRow, $upperLeft);
|
} else {
|
||||||
|
$this->clearMergeCellsByRow($firstColumn, $lastColumnIndex, $firstRow, $lastRow, $upperLeft, $behaviour);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function clearMergeCellsByColumn(string $firstColumn, string $lastColumn, int $firstRow, int $lastRow, string $upperLeft): void
|
private function clearMergeCellsByColumn(string $firstColumn, string $lastColumn, int $firstRow, int $lastRow, string $upperLeft, string $behaviour): void
|
||||||
{
|
{
|
||||||
|
$leftCellValue = [$this->getCell($upperLeft)->getFormattedValue()];
|
||||||
|
|
||||||
foreach ($this->getColumnIterator($firstColumn, $lastColumn) as $column) {
|
foreach ($this->getColumnIterator($firstColumn, $lastColumn) as $column) {
|
||||||
$iterator = $column->getCellIterator($firstRow);
|
$iterator = $column->getCellIterator($firstRow);
|
||||||
$iterator->setIterateOnlyExistingCells(true);
|
$iterator->setIterateOnlyExistingCells(true);
|
||||||
|
|
@ -1814,17 +1827,21 @@ class Worksheet implements IComparable
|
||||||
if ($row > $lastRow) {
|
if ($row > $lastRow) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$thisCell = $cell->getColumn() . $row;
|
$leftCellValue = $this->mergeCellBehaviour($cell, $upperLeft, $behaviour, $leftCellValue);
|
||||||
if ($upperLeft !== $thisCell) {
|
|
||||||
$cell->setValueExplicit(null, DataType::TYPE_NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$leftCellValue = implode(' ', $leftCellValue);
|
||||||
|
if ($behaviour === self::MERGE_CELL_CONTENT_MERGE) {
|
||||||
|
$this->getCell($upperLeft)->setValueExplicit($leftCellValue, DataType::TYPE_STRING);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function clearMergeCellsByRow(string $firstColumn, int $lastColumnIndex, int $firstRow, int $lastRow, string $upperLeft): void
|
private function clearMergeCellsByRow(string $firstColumn, int $lastColumnIndex, int $firstRow, int $lastRow, string $upperLeft, string $behaviour): void
|
||||||
{
|
{
|
||||||
|
$leftCellValue = [$this->getCell($upperLeft)->getFormattedValue()];
|
||||||
|
|
||||||
foreach ($this->getRowIterator($firstRow, $lastRow) as $row) {
|
foreach ($this->getRowIterator($firstRow, $lastRow) as $row) {
|
||||||
$iterator = $row->getCellIterator($firstColumn);
|
$iterator = $row->getCellIterator($firstColumn);
|
||||||
$iterator->setIterateOnlyExistingCells(true);
|
$iterator->setIterateOnlyExistingCells(true);
|
||||||
|
|
@ -1835,13 +1852,31 @@ class Worksheet implements IComparable
|
||||||
if ($columnIndex > $lastColumnIndex) {
|
if ($columnIndex > $lastColumnIndex) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$thisCell = $column . $cell->getRow();
|
$leftCellValue = $this->mergeCellBehaviour($cell, $upperLeft, $behaviour, $leftCellValue);
|
||||||
if ($upperLeft !== $thisCell) {
|
|
||||||
$cell->setValueExplicit(null, DataType::TYPE_NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$leftCellValue = implode(' ', $leftCellValue);
|
||||||
|
if ($behaviour === self::MERGE_CELL_CONTENT_MERGE) {
|
||||||
|
$this->getCell($upperLeft)->setValueExplicit($leftCellValue, DataType::TYPE_STRING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mergeCellBehaviour(Cell $cell, string $upperLeft, string $behaviour, array $leftCellValue): array
|
||||||
|
{
|
||||||
|
if ($cell->getCoordinate() !== $upperLeft) {
|
||||||
|
Calculation::getInstance($cell->getWorksheet()->getParent())->flushInstance();
|
||||||
|
if ($behaviour === self::MERGE_CELL_CONTENT_MERGE) {
|
||||||
|
$cellValue = $cell->getFormattedValue();
|
||||||
|
if ($cellValue !== '') {
|
||||||
|
$leftCellValue[] = $cellValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cell->setValueExplicit(null, DataType::TYPE_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $leftCellValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1856,17 +1891,22 @@ class Worksheet implements IComparable
|
||||||
* @param int $row1 Numeric row coordinate of the first cell
|
* @param int $row1 Numeric row coordinate of the first cell
|
||||||
* @param int $columnIndex2 Numeric column coordinate of the last cell
|
* @param int $columnIndex2 Numeric column coordinate of the last cell
|
||||||
* @param int $row2 Numeric row coordinate of the last cell
|
* @param int $row2 Numeric row coordinate of the last cell
|
||||||
|
* @param string $behaviour How the merged cells should behave.
|
||||||
|
* Possible values are:
|
||||||
|
* MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells
|
||||||
|
* MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
|
||||||
|
* MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
|
public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $behaviour = self::MERGE_CELL_CONTENT_EMPTY)
|
||||||
{
|
{
|
||||||
$cellRange = new CellRange(
|
$cellRange = new CellRange(
|
||||||
CellAddress::fromColumnAndRow($columnIndex1, $row1),
|
CellAddress::fromColumnAndRow($columnIndex1, $row1),
|
||||||
CellAddress::fromColumnAndRow($columnIndex2, $row2)
|
CellAddress::fromColumnAndRow($columnIndex2, $row2)
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->mergeCells($cellRange);
|
return $this->mergeCells($cellRange, $behaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class HiddenMergeCellsTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Spreadsheet
|
||||||
|
*/
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
protected function setup(): void
|
||||||
|
{
|
||||||
|
$filename = 'tests/data/Reader/Ods/HiddenMergeCellsTest.ods';
|
||||||
|
$reader = new Ods();
|
||||||
|
$this->spreadsheet = $reader->load($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHiddenMergeCells(): void
|
||||||
|
{
|
||||||
|
$c2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('C2')->isInMergeRange();
|
||||||
|
self::assertTrue($c2InMergeRange);
|
||||||
|
$a2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('A2')->isInMergeRange();
|
||||||
|
self::assertTrue($a2InMergeRange);
|
||||||
|
$a2MergeRangeValue = $this->spreadsheet->getActiveSheet()->getCell('A2')->isMergeRangeValueCell();
|
||||||
|
self::assertTrue($a2MergeRangeValue);
|
||||||
|
|
||||||
|
$cellArray = $this->spreadsheet->getActiveSheet()->rangeToArray('A2:C2');
|
||||||
|
self::assertSame([[12, 4, 3]], $cellArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnmergeHiddenMergeCells(): void
|
||||||
|
{
|
||||||
|
$this->spreadsheet->getActiveSheet()->unmergeCells('A2:C2');
|
||||||
|
|
||||||
|
$c2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('C2')->isInMergeRange();
|
||||||
|
self::assertFalse($c2InMergeRange);
|
||||||
|
$a2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('A2')->isInMergeRange();
|
||||||
|
self::assertFalse($a2InMergeRange);
|
||||||
|
|
||||||
|
$cellArray = $this->spreadsheet->getActiveSheet()->rangeToArray('A2:C2', null, false, false, false);
|
||||||
|
self::assertSame([[12, '=6-B1', '=A2/B2']], $cellArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class HiddenMergeCellsTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Spreadsheet
|
||||||
|
*/
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
protected function setup(): void
|
||||||
|
{
|
||||||
|
$filename = 'tests/data/Reader/XLS/HiddenMergeCellsTest.xls';
|
||||||
|
$reader = new Xls();
|
||||||
|
$this->spreadsheet = $reader->load($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHiddenMergeCells(): void
|
||||||
|
{
|
||||||
|
$c2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('C2')->isInMergeRange();
|
||||||
|
self::assertTrue($c2InMergeRange);
|
||||||
|
$a2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('A2')->isInMergeRange();
|
||||||
|
self::assertTrue($a2InMergeRange);
|
||||||
|
$a2MergeRangeValue = $this->spreadsheet->getActiveSheet()->getCell('A2')->isMergeRangeValueCell();
|
||||||
|
self::assertTrue($a2MergeRangeValue);
|
||||||
|
|
||||||
|
$cellArray = $this->spreadsheet->getActiveSheet()->rangeToArray('A2:C2');
|
||||||
|
self::assertSame([[12, 4, 3]], $cellArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnmergeHiddenMergeCells(): void
|
||||||
|
{
|
||||||
|
$this->spreadsheet->getActiveSheet()->unmergeCells('A2:C2');
|
||||||
|
|
||||||
|
$c2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('C2')->isInMergeRange();
|
||||||
|
self::assertFalse($c2InMergeRange);
|
||||||
|
$a2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('A2')->isInMergeRange();
|
||||||
|
self::assertFalse($a2InMergeRange);
|
||||||
|
|
||||||
|
$cellArray = $this->spreadsheet->getActiveSheet()->rangeToArray('A2:C2', null, false, false, false);
|
||||||
|
self::assertSame([[12, '=6-B1', '=A2/B2']], $cellArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class HiddenMergeCellsTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Spreadsheet
|
||||||
|
*/
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
protected function setup(): void
|
||||||
|
{
|
||||||
|
$filename = 'tests/data/Reader/XLSX/HiddenMergeCellsTest.xlsx';
|
||||||
|
$reader = new Xlsx();
|
||||||
|
$this->spreadsheet = $reader->load($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHiddenMergeCells(): void
|
||||||
|
{
|
||||||
|
$c2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('C2')->isInMergeRange();
|
||||||
|
self::assertTrue($c2InMergeRange);
|
||||||
|
$a2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('A2')->isInMergeRange();
|
||||||
|
self::assertTrue($a2InMergeRange);
|
||||||
|
$a2MergeRangeValue = $this->spreadsheet->getActiveSheet()->getCell('A2')->isMergeRangeValueCell();
|
||||||
|
self::assertTrue($a2MergeRangeValue);
|
||||||
|
|
||||||
|
$cellArray = $this->spreadsheet->getActiveSheet()->rangeToArray('A2:C2');
|
||||||
|
self::assertSame([[12, 4, 3]], $cellArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnmergeHiddenMergeCells(): void
|
||||||
|
{
|
||||||
|
$this->spreadsheet->getActiveSheet()->unmergeCells('A2:C2');
|
||||||
|
|
||||||
|
$c2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('C2')->isInMergeRange();
|
||||||
|
self::assertFalse($c2InMergeRange);
|
||||||
|
$a2InMergeRange = $this->spreadsheet->getActiveSheet()->getCell('A2')->isInMergeRange();
|
||||||
|
self::assertFalse($a2InMergeRange);
|
||||||
|
|
||||||
|
$cellArray = $this->spreadsheet->getActiveSheet()->rangeToArray('A2:C2', null, false, false, false);
|
||||||
|
self::assertSame([[12, '=6-B1', '=A2/B2']], $cellArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
|
||||||
|
|
||||||
|
class MergeBehaviourTest extends AbstractFunctional
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $testDataRaw = [
|
||||||
|
[1.1, 2.2, '=ROUND(A1+B1, 1)'],
|
||||||
|
[4.4, 5.5, '=ROUND(A2+B2, 1)'],
|
||||||
|
['=ROUND(A1+A2, 1)', '=ROUND(B1+B2, 1)', '=ROUND(A3+B3, 1)'],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $testDataFormatted = [
|
||||||
|
['=DATE(1960, 12, 19)', '=DATE(2022, 09, 15)'],
|
||||||
|
];
|
||||||
|
|
||||||
|
public function testMergeCellsDefaultBehaviour(): void
|
||||||
|
{
|
||||||
|
$expectedResult = [
|
||||||
|
[1.1, null, null],
|
||||||
|
[null, null, null],
|
||||||
|
[null, null, null],
|
||||||
|
];
|
||||||
|
|
||||||
|
$mergeRange = 'A1:C3';
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
$worksheet->fromArray($this->testDataRaw, null, 'A1', true);
|
||||||
|
$worksheet->mergeCells($mergeRange);
|
||||||
|
|
||||||
|
$mergeResult = $worksheet->toArray(null, true, true, false);
|
||||||
|
self::assertSame($expectedResult, $mergeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeCellsDefaultBehaviourFormatted(): void
|
||||||
|
{
|
||||||
|
$expectedResult = [
|
||||||
|
['1960-12-19', null],
|
||||||
|
];
|
||||||
|
|
||||||
|
$mergeRange = 'A1:B1';
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
$worksheet->fromArray($this->testDataFormatted, null, 'A1', true);
|
||||||
|
$worksheet->getStyle($mergeRange)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
|
||||||
|
$worksheet->mergeCells($mergeRange);
|
||||||
|
|
||||||
|
$mergeResult = $worksheet->toArray(null, true, true, false);
|
||||||
|
self::assertSame($expectedResult, $mergeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeCellsHideBehaviour(): void
|
||||||
|
{
|
||||||
|
$expectedResult = [
|
||||||
|
[1.1, 2.2, 3.3],
|
||||||
|
[4.4, 5.5, 9.9],
|
||||||
|
[5.5, 7.7, 13.2],
|
||||||
|
];
|
||||||
|
|
||||||
|
$mergeRange = 'A1:C3';
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
$worksheet->fromArray($this->testDataRaw, null, 'A1', true);
|
||||||
|
$worksheet->mergeCells($mergeRange, Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
|
|
||||||
|
$mergeResult = $worksheet->toArray(null, true, true, false);
|
||||||
|
self::assertSame($expectedResult, $mergeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeCellsHideBehaviourFormatted(): void
|
||||||
|
{
|
||||||
|
$expectedResult = [
|
||||||
|
['1960-12-19', '2022-09-15'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$mergeRange = 'A1:B1';
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
$worksheet->fromArray($this->testDataFormatted, null, 'A1', true);
|
||||||
|
$worksheet->getStyle($mergeRange)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
|
||||||
|
$worksheet->mergeCells($mergeRange, Worksheet::MERGE_CELL_CONTENT_HIDE);
|
||||||
|
|
||||||
|
$mergeResult = $worksheet->toArray(null, true, true, false);
|
||||||
|
self::assertSame($expectedResult, $mergeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider mergeCellsMergeBehaviourProvider
|
||||||
|
*/
|
||||||
|
public function testMergeCellsMergeBehaviour(array $testData, string $mergeRange, array $expectedResult): void
|
||||||
|
{
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
$worksheet->fromArray($testData, null, 'A1', true);
|
||||||
|
// Force a precalculation to populate the calculation cache, so that we can verify that it is being cleared
|
||||||
|
$worksheet->toArray();
|
||||||
|
$worksheet->mergeCells($mergeRange, Worksheet::MERGE_CELL_CONTENT_MERGE);
|
||||||
|
|
||||||
|
$mergeResult = $worksheet->toArray(null, true, true, false);
|
||||||
|
self::assertSame($expectedResult, $mergeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mergeCellsMergeBehaviourProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'With Calculated Values' => [
|
||||||
|
$this->testDataRaw,
|
||||||
|
'A1:C3',
|
||||||
|
[
|
||||||
|
['1.1 2.2 1.1 4.4 5.5 0 1.1 0 0', null, null],
|
||||||
|
[null, null, null],
|
||||||
|
[null, null, null],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'With Empty Cells' => [
|
||||||
|
[
|
||||||
|
[1, '', 2],
|
||||||
|
[null, 3, null],
|
||||||
|
[4, null, 5],
|
||||||
|
],
|
||||||
|
'A1:C3',
|
||||||
|
[
|
||||||
|
['1 2 3 4 5', null, null],
|
||||||
|
[null, null, null],
|
||||||
|
[null, null, null],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[12, '=5+1', '=A1/A2'],
|
||||||
|
],
|
||||||
|
'A1:C1',
|
||||||
|
[
|
||||||
|
['12 6 #DIV/0!', null, null],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeCellsMergeBehaviourFormatted(): void
|
||||||
|
{
|
||||||
|
$expectedResult = [
|
||||||
|
['1960-12-19 2022-09-15', null],
|
||||||
|
];
|
||||||
|
|
||||||
|
$mergeRange = 'A1:B1';
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
$worksheet->fromArray($this->testDataFormatted, null, 'A1', true);
|
||||||
|
$worksheet->getStyle($mergeRange)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
|
||||||
|
$worksheet->mergeCells($mergeRange, Worksheet::MERGE_CELL_CONTENT_MERGE);
|
||||||
|
|
||||||
|
$mergeResult = $worksheet->toArray(null, true, true, false);
|
||||||
|
self::assertSame($expectedResult, $mergeResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue