Skip to content

Commit

Permalink
Merge pull request #4076 from oleibman/pr1415
Browse files Browse the repository at this point in the history
CSV Writer Allow Varying Number of Columns
  • Loading branch information
oleibman committed Jun 30, 2024
2 parents aae4992 + e8bb091 commit 14c3df0
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 1 deletion.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added

- Xlsx Reader Optionally Ignore Rows With No Cells. [Issue #3982](https://github.com/PHPOffice/PhpSpreadsheet/issues/3982) [PR #4035](https://github.com/PHPOffice/PhpSpreadsheet/pull/4035)
- Option for CSV output file to have varying numbers of columns for each row. [Issue #1415](https://github.com/PHPOffice/PhpSpreadsheet/issues/1415) [PR #4076](https://github.com/PHPOffice/PhpSpreadsheet/pull/4076)

### Changed

Expand All @@ -33,7 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Mpdf and Tcpdf Borders on Merged Cells. [Issue #3557](https://github.com/PHPOffice/PhpSpreadsheet/issues/3557) [PR #4047](https://github.com/PHPOffice/PhpSpreadsheet/pull/4047)
- Xls Conditional Format Improvements. [PR #4030](https://github.com/PHPOffice/PhpSpreadsheet/pull/4030) [PR #4033](https://github.com/PHPOffice/PhpSpreadsheet/pull/4033)
- Conditional Range Unions and Intersections [Issue #4039](https://github.com/PHPOffice/PhpSpreadsheet/issues/4039) [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4049](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4040](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
- Problem rendering line chart with missing plot label. [PR #4074](https://github.com/PHPOffice/PhpSpreadsheet/pull/4074)
- More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065)
- Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064)

Expand Down
12 changes: 12 additions & 0 deletions docs/topics/reading-and-writing-to-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,18 @@ $writer->setOutputEncoding('SJIS-WIN');
$writer->save("05featuredemo.csv");
```

#### Writing CSV files with varying numbers of columns

A CSV file can have a different number of columns in each row. This
differs from the default behavior when saving as a .csv in Excel, but
can be enabled in PhpSpreadsheet by using the following code:

``` php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setVariableColumns(true);
$writer->save("05featuredemo.csv");
```

#### Decimal and thousands separators

If the worksheet you are exporting contains numbers with decimal or
Expand Down
40 changes: 40 additions & 0 deletions src/PhpSpreadsheet/Writer/Csv.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer;

use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Stringable;

Expand Down Expand Up @@ -54,6 +55,13 @@ class Csv extends BaseWriter
*/
private string $outputEncoding = '';

/**
* Whether number of columns should be allowed to vary
* between rows, or use a fixed range based on the max
* column overall.
*/
private bool $variableColumns = false;

/**
* Create a new CSV.
*/
Expand Down Expand Up @@ -105,7 +113,17 @@ public function save($filename, int $flags = 0): void
$maxRow = $sheet->getHighestDataRow();

// Write rows to file
$row = 0;
foreach ($sheet->rangeToArrayYieldRows("A1:$maxCol$maxRow", '', $this->preCalculateFormulas) as $cellsArray) {
++$row;
if ($this->variableColumns) {
$column = $sheet->getHighestDataColumn($row);
if ($column === 'A' && !$sheet->cellExists("A$row")) {
$cellsArray = [];
} else {
array_splice($cellsArray, Coordinate::columnIndexFromString($column));
}
}
$this->writeLine($this->fileHandle, $cellsArray);
}

Expand Down Expand Up @@ -303,4 +321,26 @@ private function writeLine($fileHandle, array $values): void
}
fwrite($fileHandle, $line);
}

/**
* Get whether number of columns should be allowed to vary
* between rows, or use a fixed range based on the max
* column overall.
*/
public function getVariableColumns(): bool
{
return $this->variableColumns;
}

/**
* Set whether number of columns should be allowed to vary
* between rows, or use a fixed range based on the max
* column overall.
*/
public function setVariableColumns(bool $pValue): self
{
$this->variableColumns = $pValue;

return $this;
}
}
81 changes: 81 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Csv/VariableColumnsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Csv;

use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
use PHPUnit\Framework\TestCase;

class VariableColumnsTest extends TestCase
{
public function testVariableColumns(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray(
[
[1, 2, 3, 4],
[1, 2],
[1, 2, 3, 4, 5],
[],
[1],
[1, 2, 3],
]
);

$filename = File::temporaryFilename();
$writer = new Csv($spreadsheet);
$writer->setVariableColumns(true);
$writer->save($filename);

$contents = (string) file_get_contents($filename);
unlink($filename);
$spreadsheet->disconnectWorksheets();

$rows = explode(PHP_EOL, $contents);

self::assertSame('"1","2","3","4"', $rows[0]);
self::assertSame('"1","2"', $rows[1]);
self::assertSame('"1","2","3","4","5"', $rows[2]);
self::assertSame('', $rows[3]);
self::assertSame('"1"', $rows[4]);
self::assertSame('"1","2","3"', $rows[5]);
}

public function testFixedColumns(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray(
[
[1, 2, 3, 4],
[1, 2],
[1, 2, 3, 4, 5],
[],
[1],
[1, 2, 3],
]
);

$filename = File::temporaryFilename();
$writer = new Csv($spreadsheet);
self::assertFalse($writer->getVariableColumns());
$writer->save($filename);

$contents = (string) file_get_contents($filename);
unlink($filename);
$spreadsheet->disconnectWorksheets();

$rows = explode(PHP_EOL, $contents);

self::assertSame('"1","2","3","4",""', $rows[0]);
self::assertSame('"1","2","","",""', $rows[1]);
self::assertSame('"1","2","3","4","5"', $rows[2]);
self::assertSame('"","","","",""', $rows[3]);
self::assertSame('"1","","","",""', $rows[4]);
self::assertSame('"1","2","3","",""', $rows[5]);
}
}

0 comments on commit 14c3df0

Please sign in to comment.