Skip to content

Commit

Permalink
Introduce value formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
phansys committed Oct 23, 2023
1 parent 0b8c67d commit b07d23b
Show file tree
Hide file tree
Showing 18 changed files with 433 additions and 10 deletions.
28 changes: 28 additions & 0 deletions src/Bridge/Symfony/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultValue(false)
->info('include the byte order mark')
->end()
->arrayNode('formatters')
->defaultValue($this->getDefaultFormatters())
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('json')
Expand All @@ -84,6 +88,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultValue('php://output')
->info('path to the output file')
->end()
->arrayNode('formatters')
->defaultValue($this->getDefaultFormatters())
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('xls')
Expand All @@ -97,6 +105,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultValue(true)
->info('add column names as the first line')
->end()
->arrayNode('formatters')
->defaultValue($this->getDefaultFormatters())
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('xlsx')
Expand All @@ -114,6 +126,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultValue(true)
->info('add filters in the first line')
->end()
->arrayNode('formatters')
->defaultValue($this->getDefaultFormatters())
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('xml')
Expand All @@ -135,6 +151,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultValue('data')
->info('name of elements corresponding to rows')
->end()
->arrayNode('formatters')
->defaultValue($this->getDefaultFormatters())
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
Expand All @@ -157,4 +177,12 @@ private function getDefaultWriters(): array

return $fields;
}

/**
* @return string[]
*/
private function getDefaultFormatters(): array
{
return ['datetime', 'dateinterval', 'bool', 'enum', 'stringable'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

/**
Expand Down Expand Up @@ -60,6 +61,14 @@ private function configureExporter(ContainerBuilder $container, array $config):
private function configureWriters(ContainerBuilder $container, array $config): void
{
foreach ($config as $format => $settings) {
if ($container->hasDefinition('sonata.exporter.writer.'.$format)) {
$writer = $container->getDefinition('sonata.exporter.writer.'.$format);

foreach ($config[$format]['formatters'] as $formatter) {
$writer->addMethodCall('addFormatter', [new Reference('sonata.exporter.formatter.'.$formatter)]);
}
}

foreach ($settings as $key => $value) {
$container->setParameter(sprintf(
'sonata.exporter.writer.%s.%s',
Expand Down
7 changes: 7 additions & 0 deletions src/Bridge/Symfony/Resources/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Sonata\Exporter\Exporter;
use Sonata\Exporter\ExporterInterface;
use Sonata\Exporter\Formatter\BoolFormatter;
use Sonata\Exporter\Formatter\EnumFormatter;
use Sonata\Exporter\Writer\CsvWriter;
use Sonata\Exporter\Writer\JsonWriter;
use Sonata\Exporter\Writer\XlsWriter;
Expand Down Expand Up @@ -67,4 +69,9 @@

$services->alias(Exporter::class, 'sonata.exporter.exporter');
$services->alias(ExporterInterface::class, 'sonata.exporter.exporter');

$services->set('sonata.exporter.formatter.bool', BoolFormatter::class)
->tag('sonata.exporter.formatter');
$services->set('sonata.exporter.formatter.enum', EnumFormatter::class)
->tag('sonata.exporter.formatter');
};
39 changes: 39 additions & 0 deletions src/Formatter/BoolFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

final class BoolFormatter implements FormatterInterface
{
private const LABEL_TRUE = 'yes';
private const LABEL_FALSE = 'no';

public function __construct(
private string $trueLabel = self::LABEL_TRUE,
private string $falseLabel = self::LABEL_FALSE
) {
}

public function format(array $data): array

Check failure on line 27 in src/Formatter/BoolFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\BoolFormatter::format() has parameter $data with no value type specified in iterable type array.

Check failure on line 27 in src/Formatter/BoolFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\BoolFormatter::format() return type has no value type specified in iterable type array.
{
foreach ($data as $key => $value) {
if (!\is_bool($value)) {
continue;
}

$data[$key] = $value ? $this->trueLabel : $this->falseLabel;
}

return $data;
}
}
69 changes: 69 additions & 0 deletions src/Formatter/DateIntervalFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

final class DateIntervalFormatter implements FormatterInterface
{
private const DATE_PARTS = [
'y' => 'Y',
'm' => 'M',
'd' => 'D',
];
private const TIME_PARTS = [
'h' => 'H',
'i' => 'M',
's' => 'S',
];

public function format(array $data): array

Check failure on line 29 in src/Formatter/DateIntervalFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\DateIntervalFormatter::format() has parameter $data with no value type specified in iterable type array.

Check failure on line 29 in src/Formatter/DateIntervalFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\DateIntervalFormatter::format() return type has no value type specified in iterable type array.
{
foreach ($data as $key => $value) {
if (!$value instanceof \DateInterval) {
continue;
}

$data[$key] = self::getDuration($value);
}

return $data;
}

/**
* @return string An ISO8601 duration
*/
private static function getDuration(\DateInterval $interval): string
{
$datePart = '';

foreach (self::DATE_PARTS as $datePartAttribute => $datePartAttributeString) {
if ($interval->$datePartAttribute !== 0) {
$datePart .= $interval->$datePartAttribute.$datePartAttributeString;
}
}

$timePart = '';

foreach (self::TIME_PARTS as $timePartAttribute => $timePartAttributeString) {
if ($interval->$timePartAttribute !== 0) {
$timePart .= $interval->$timePartAttribute.$timePartAttributeString;
}
}

if ('' === $datePart && '' === $timePart) {
return 'P0Y';
}

return 'P'.$datePart.('' !== $timePart ? 'T'.$timePart : '');
}
}
35 changes: 35 additions & 0 deletions src/Formatter/DateTimeFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

final class DateTimeFormatter implements FormatterInterface
{
public function __construct(
private string $dateTimeFormat = \DateTimeInterface::RFC2822
) {
}

public function format(array $data): array

Check failure on line 23 in src/Formatter/DateTimeFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\DateTimeFormatter::format() has parameter $data with no value type specified in iterable type array.

Check failure on line 23 in src/Formatter/DateTimeFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\DateTimeFormatter::format() return type has no value type specified in iterable type array.
{
foreach ($data as $key => $value) {
if (!$value instanceof \DateTimeInterface) {
continue;
}

$data[$key] = $value->format($this->dateTimeFormat);
}

return $data;
}
}
41 changes: 41 additions & 0 deletions src/Formatter/EnumFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

final class EnumFormatter implements FormatterInterface
{
public function __construct(
private bool $useBackedEnumValue = true
) {
}

public function format(array $data): array

Check failure on line 23 in src/Formatter/EnumFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\EnumFormatter::format() has parameter $data with no value type specified in iterable type array.

Check failure on line 23 in src/Formatter/EnumFormatter.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\EnumFormatter::format() return type has no value type specified in iterable type array.
{
foreach ($data as $key => $value) {
if (!$value instanceof \UnitEnum) {
continue;
}

if ($this->useBackedEnumValue && $value instanceof \BackedEnum) {
$data[$key] = $value->value;

continue;
}

$data[$key] = $value->name;
}

return $data;
}
}
19 changes: 19 additions & 0 deletions src/Formatter/FormatterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

interface FormatterInterface
{
public function format(array $data): array;

Check failure on line 18 in src/Formatter/FormatterInterface.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\FormatterInterface::format() has parameter $data with no value type specified in iterable type array.

Check failure on line 18 in src/Formatter/FormatterInterface.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\Exporter\Formatter\FormatterInterface::format() return type has no value type specified in iterable type array.
}
49 changes: 49 additions & 0 deletions src/Formatter/IterableFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

final class IterableFormatter implements FormatterInterface
{
public function __construct(
private array $formatters = []
) {
}

public function format(array $data, array $formatters = []): array
{
$this->formatters = $formatters;
foreach ($data as $key => $value) {
if (!is_iterable($value)) {
continue;
}

if ($value instanceof \Iterator) {
$value = iterator_to_array($value);
}

$data[$key] = '['.implode(', ', array_map([$this, 'formatFromIterable'], $value)).']';

Check failure on line 35 in src/Formatter/IterableFormatter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidArgument

src/Formatter/IterableFormatter.php:35:86: InvalidArgument: Argument 2 of array_map expects array<array-key, mixed>, but iterable<mixed, mixed> provided (see https://psalm.dev/004)

Check failure on line 35 in src/Formatter/IterableFormatter.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidArrayAccess

src/Formatter/IterableFormatter.php:35:86: InvalidArrayAccess: Cannot access array value on non-array variable $value of type iterable<mixed, mixed> (see https://psalm.dev/005)
}

return $data;
}

private function formatFromIterable(mixed $value): mixed
{
foreach ($this->formatters as $formatter) {
$value = $formatter->format($value, $this->formatters);
}

return $value;
}
}
30 changes: 30 additions & 0 deletions src/Formatter/StringableFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\Exporter\Formatter;

final class StringableFormatter implements FormatterInterface
{
public function format(array $data): array
{
foreach ($data as $key => $value) {
if (!$value instanceof \Stringable) {
continue;
}

$data[$key] = (string) $value;
}

return $data;
}
}
Loading

0 comments on commit b07d23b

Please sign in to comment.