Skip to content

Commit

Permalink
Adding a formatter to all Converter
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Dec 10, 2024
1 parent f81df48 commit 42a8b36
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 6 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

All Notable changes to `Csv` will be documented in this file

## [Next](https://github.com/thephpleague/csv/compare/9.19.0...master) - TBD

### Added

- `XMLConverter::formatter`
- `HTMLConverter::formatter`

### Deprecated

- None

### Fixed

- `JsonConverter::formatter` now accepts callable before only `Closure` where accepted.

### Removed

- None

## [9.19.0](https://github.com/thephpleague/csv/compare/9.18.0...9.19.0) - 2024-12-08

### Added
Expand Down
16 changes: 16 additions & 0 deletions docs/9.0/converter/html.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ This method sets the optional attribute name for the field name on the HTML `td`

<p class="message-info">If none is used or an empty string is given, the field name information won't be exported to the HTML table.</p>


### HTMLConverter::formatter

<p class="message-info">New feature introduced in version <code>9.20.0</code></p>

```php
public HTMLConverter::formatter(?callback $formatter): mixed
```

This method allow to apply a callback prior to converting your collection individual item.
This callback allows you to specify how each item will be converted. The formatter should
return an associative array suitable for conversion.

<p class="message-notice">The <code>Formatter</code> callback does not affect the footer
and/or header conversion.</p>

## Conversion

<p class="message-info">Since version <code>9.3.0</code> this method accepts optional header and footer records to display them in the exported HTML table.</p>
Expand Down
12 changes: 12 additions & 0 deletions docs/9.0/converter/xml.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ This method sets the XML field name and optionally the attribute name for the fi
<p class="message-info">The default field element name is <code>cell</code>.</p>
<p class="message-info">The default attribute name is an empty string.</p>

### XMLConverter::formatter

<p class="message-info">New feature introduced in version <code>9.20.0</code></p>

```php
public XMLConverter::formatter(?callback $formatter): mixed
```

This method allow to apply a callback prior to converting your collection individual item.
This callback allows you to specify how each item will be converted. The formatter should
return an associative array suitable for conversion.

## Conversion

```php
Expand Down
19 changes: 19 additions & 0 deletions src/HTMLConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class HTMLConverter
/** table id attribute value. */
protected string $id_value = '';
protected XMLConverter $xml_converter;
/** @var ?callable(array, array-key): array */
protected mixed $formatter = null;

public static function create(): self
{
Expand Down Expand Up @@ -60,6 +62,10 @@ public function __construct()
*/
public function convert(iterable $records, array $header_record = [], array $footer_record = []): string
{
if (null !== $this->formatter) {
$records = MapIterator::fromIterable($records, $this->formatter);
}

$doc = new DOMDocument('1.0');
if ([] === $header_record && [] === $footer_record) {
$table = $this->xml_converter->import($records, $doc);
Expand Down Expand Up @@ -159,4 +165,17 @@ public function td(string $fieldname_attribute_name): self

return $clone;
}

/**
* Set a callback to format each item before json encode.
*
* @param ?callable(array, array-key): array $formatter
*/
public function formatter(?callable $formatter): self
{
$clone = clone $this;
$clone->formatter = $formatter;

return $clone;
}
}
27 changes: 27 additions & 0 deletions src/HTMLConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;

use function array_map;

#[Group('converter')]
final class HTMLConverterTest extends TestCase
{
Expand Down Expand Up @@ -147,4 +149,29 @@ public function testTableTriggersException(): void
$this->expectException(DOMException::class);
HTMLConverter::create()->table('table-csv-data', 'te st');
}

public function testToHTMLWithFormatter(): void
{
$csv = Reader::createFromPath(__DIR__.'/../test_files/prenoms.csv', 'r')
->setDelimiter(';')
->setHeaderOffset(0)
;

$stmt = Statement::create()
->offset(3)
->limit(5)
;

$records = $stmt->process($csv);

$converter = HTMLConverter::create()
->table('table-csv-data', 'test')
->td('title')
->tr('data-record-offset')
->formatter(fn (array $record, int|string $key): array => array_map(strtoupper(...), $record));
;

$html = $converter->convert($records);
self::assertStringContainsString('ABEL', $html);
}
}
10 changes: 5 additions & 5 deletions src/JsonConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ final class JsonConverter
public readonly int $depth;
/** @var int<1, max> */
public readonly int $indentSize;
/** @var ?Closure(T, array-key): mixed */
public readonly ?Closure $formatter;
/** @var ?callable(T, array-key): mixed */
public readonly mixed $formatter;
/** @var int<1, max> */
public readonly int $chunkSize;
/** @var non-empty-string */
Expand Down Expand Up @@ -135,12 +135,12 @@ public static function create(): self
/**
* @param int<1, max> $depth
* @param int<1, max> $indentSize
* @param ?Closure(T, array-key): mixed $formatter
* @param ?callable(T, array-key): mixed $formatter
* @param int<1, max> $chunkSize
*
* @throws InvalidArgumentException
*/
private function __construct(int $flags, int $depth, int $indentSize, ?Closure $formatter, int $chunkSize)
private function __construct(int $flags, int $depth, int $indentSize, ?callable $formatter, int $chunkSize)
{
json_encode([], $flags & ~JSON_THROW_ON_ERROR, $depth);

Expand Down Expand Up @@ -345,7 +345,7 @@ public function chunkSize(int $chunkSize): self
/**
* Set a callback to format each item before json encode.
*/
public function formatter(?Closure $formatter): self
public function formatter(?callable $formatter): self
{
return new self($this->flags, $this->depth, $this->indentSize, $formatter, $this->chunkSize);
}
Expand Down
20 changes: 19 additions & 1 deletion src/XMLConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

namespace League\Csv;

use Deprecated;
use DOMAttr;
use DOMDocument;
use DOMElement;
Expand All @@ -35,6 +34,8 @@ class XMLConverter
protected string $column_attr = '';
/** XML offset attribute name. */
protected string $offset_attr = '';
/** @var ?callable(array, array-key): array */
public mixed $formatter = null;

public static function create(): self
{
Expand All @@ -56,6 +57,10 @@ public function __construct()
*/
public function convert(iterable $records): DOMDocument
{
if (null !== $this->formatter) {
$records = MapIterator::fromIterable($records, $this->formatter);
}

$doc = new DOMDocument('1.0');
$node = $this->import($records, $doc);
$doc->appendChild($node);
Expand Down Expand Up @@ -148,6 +153,19 @@ public function rootElement(string $node_name): self
return $clone;
}

/**
* Set a callback to format each item before json encode.
*
* @param ?callable(array, array-key): array $formatter
*/
public function formatter(?callable $formatter): self
{
$clone = clone $this;
$clone->formatter = $formatter;

return $clone;
}

/**
* Filters XML element name.
*
Expand Down
26 changes: 26 additions & 0 deletions src/XMLConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;

use function array_map;

#[Group('converter')]
final class XMLConverterTest extends TestCase
{
Expand Down Expand Up @@ -117,4 +119,28 @@ public function testDownload(): void
self::assertStringContainsString('content-disposition: attachment;filename="foobar.xml"', $headers[3]);
self::assertSame($xml, $output);
}

public function testToXMLWithFormatter(): void
{
$csv = Reader::createFromPath(__DIR__.'/../test_files/prenoms.csv', 'r')
->setDelimiter(';')
->setHeaderOffset(0)
;

$stmt = Statement::create()
->offset(3)
->limit(5)
;

$records = $stmt->process($csv);

$converter = XMLConverter::create()
->rootElement('csv')
->recordElement('record', 'offset')
->fieldElement('field', 'name')
->formatter(fn (array $record, int|string $key): array => array_map(strtoupper(...), $record));
;

self::assertStringContainsString('ABEL', (string) $converter->convert($records)->saveXML());
}
}

0 comments on commit 42a8b36

Please sign in to comment.