mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-07-03 15:36:14 +00:00
Refactor spreadsheetformatter so that the generate method becomes pure + add test
This commit is contained in:
parent
af292beecb
commit
be437c8784
@ -12,110 +12,25 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Export\Formatter;
|
namespace Chill\MainBundle\Export\Formatter;
|
||||||
|
|
||||||
use Chill\MainBundle\Export\ExportGenerationContext;
|
use Chill\MainBundle\Export\ExportGenerationContext;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
use Chill\MainBundle\Export\ExportManagerAwareInterface;
|
use Chill\MainBundle\Export\ExportManagerAwareInterface;
|
||||||
|
use Chill\MainBundle\Export\FormattedExportGeneration;
|
||||||
use Chill\MainBundle\Export\FormatterInterface;
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
use Chill\MainBundle\Export\Helper\ExportManagerAwareTrait;
|
use Chill\MainBundle\Export\Helper\ExportManagerAwareTrait;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Contracts\Translation\TranslatableInterface;
|
use Symfony\Contracts\Translation\TranslatableInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInterface
|
final class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInterface
|
||||||
{
|
{
|
||||||
use ExportManagerAwareTrait;
|
use ExportManagerAwareTrait;
|
||||||
|
|
||||||
/**
|
public function __construct(private readonly TranslatorInterface $translator) {}
|
||||||
* an array where keys are the aggregators aliases and
|
|
||||||
* values are the data.
|
|
||||||
*
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*/
|
|
||||||
protected array $aggregatorsData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The export.
|
|
||||||
*
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* @var \Chill\MainBundle\Export\ExportInterface
|
|
||||||
*/
|
|
||||||
protected $export;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* array containing value of export form.
|
|
||||||
*
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $exportData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $filtersData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $formatterData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result, as returned by the export.
|
|
||||||
*
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $result;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* replaced when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
// protected $labels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* temporary file to store spreadsheet.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $tempfile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
protected $translator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cache for displayable result.
|
|
||||||
*
|
|
||||||
* This cache is reset when `getResponse` is called.
|
|
||||||
*
|
|
||||||
* The array's keys are the keys in the raw result, and
|
|
||||||
* values are the callable which will transform the raw result to
|
|
||||||
* displayable result.
|
|
||||||
*/
|
|
||||||
private ?array $cacheDisplayableResult = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whethe `cacheDisplayableResult` is initialized or not.
|
|
||||||
*/
|
|
||||||
private bool $cacheDisplayableResultIsInitialized = false;
|
|
||||||
|
|
||||||
public function __construct(TranslatorInterface $translatorInterface)
|
|
||||||
{
|
|
||||||
$this->translator = $translatorInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildForm(
|
public function buildForm(
|
||||||
FormBuilderInterface $builder,
|
FormBuilderInterface $builder,
|
||||||
@ -178,6 +93,51 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
return 'SpreadSheet (xlsx, ods)';
|
return 'SpreadSheet (xlsx, ods)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generate(
|
||||||
|
$result,
|
||||||
|
$formatterData,
|
||||||
|
string $exportAlias,
|
||||||
|
array $exportData,
|
||||||
|
array $filtersData,
|
||||||
|
array $aggregatorsData,
|
||||||
|
ExportGenerationContext $context,
|
||||||
|
) {
|
||||||
|
// Initialize local variables instead of class properties
|
||||||
|
/** @var ExportInterface $export */
|
||||||
|
$export = $this->getExportManager()->getExport($exportAlias);
|
||||||
|
|
||||||
|
// Initialize cache variables
|
||||||
|
$cacheDisplayableResult = $this->initializeDisplayable($result, $export, $exportData, $aggregatorsData);
|
||||||
|
|
||||||
|
$tempfile = \tempnam(\sys_get_temp_dir(), '');
|
||||||
|
|
||||||
|
if (false === $tempfile) {
|
||||||
|
throw new \RuntimeException('Unable to create temporary file');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->generateContent(
|
||||||
|
$context,
|
||||||
|
$tempfile,
|
||||||
|
$result,
|
||||||
|
$formatterData,
|
||||||
|
$export,
|
||||||
|
$exportData,
|
||||||
|
$filtersData,
|
||||||
|
$aggregatorsData,
|
||||||
|
$cacheDisplayableResult,
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = new FormattedExportGeneration(
|
||||||
|
file_get_contents($tempfile),
|
||||||
|
$this->getContentType($formatterData['format']),
|
||||||
|
);
|
||||||
|
|
||||||
|
// remove the temp file from disk
|
||||||
|
\unlink($tempfile);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function getResponse(
|
public function getResponse(
|
||||||
$result,
|
$result,
|
||||||
$formatterData,
|
$formatterData,
|
||||||
@ -187,33 +147,10 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
array $aggregatorsData,
|
array $aggregatorsData,
|
||||||
ExportGenerationContext $context,
|
ExportGenerationContext $context,
|
||||||
): Response {
|
): Response {
|
||||||
// store all data when the process is initiated
|
$formattedResult = $this->generate($result, $formatterData, $exportAlias, $exportData, $filtersData, $aggregatorsData, $context);
|
||||||
$this->result = $result;
|
|
||||||
$this->formatterData = $formatterData;
|
|
||||||
$this->export = $this->getExportManager()->getExport($exportAlias);
|
|
||||||
$this->exportData = $exportData;
|
|
||||||
$this->filtersData = $filtersData;
|
|
||||||
$this->aggregatorsData = $aggregatorsData;
|
|
||||||
|
|
||||||
// reset cache
|
$response = new BinaryFileResponse($formattedResult->content);
|
||||||
$this->cacheDisplayableResult = [];
|
$response->headers->set('Content-Type', $formattedResult->contentType);
|
||||||
$this->cacheDisplayableResultIsInitialized = false;
|
|
||||||
|
|
||||||
$response = new Response();
|
|
||||||
$response->headers->set(
|
|
||||||
'Content-Type',
|
|
||||||
$this->getContentType($this->formatterData['format'])
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->tempfile = \tempnam(\sys_get_temp_dir(), '');
|
|
||||||
$this->generateContent($context);
|
|
||||||
|
|
||||||
$f = \fopen($this->tempfile, 'rb');
|
|
||||||
$response->setContent(\stream_get_contents($f));
|
|
||||||
fclose($f);
|
|
||||||
|
|
||||||
// remove the temp file from disk
|
|
||||||
\unlink($this->tempfile);
|
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
@ -223,7 +160,7 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
return 'tabular';
|
return 'tabular';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function addContentTable(
|
private function addContentTable(
|
||||||
Worksheet $worksheet,
|
Worksheet $worksheet,
|
||||||
$sortedResults,
|
$sortedResults,
|
||||||
$line,
|
$line,
|
||||||
@ -245,11 +182,11 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
*
|
*
|
||||||
* @return int the line number after the last description
|
* @return int the line number after the last description
|
||||||
*/
|
*/
|
||||||
protected function addFiltersDescription(Worksheet &$worksheet, ExportGenerationContext $context)
|
private function addFiltersDescription(Worksheet &$worksheet, ExportGenerationContext $context, array $filtersData)
|
||||||
{
|
{
|
||||||
$line = 3;
|
$line = 3;
|
||||||
|
|
||||||
foreach ($this->filtersData as $alias => $data) {
|
foreach ($filtersData as $alias => $data) {
|
||||||
$filter = $this->getExportManager()->getFilter($alias);
|
$filter = $this->getExportManager()->getFilter($alias);
|
||||||
$description = $filter->describeAction($data, $context);
|
$description = $filter->describeAction($data, $context);
|
||||||
if (\is_array($description)) {
|
if (\is_array($description)) {
|
||||||
@ -274,26 +211,22 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
*
|
*
|
||||||
* return the line number where the next content (i.e. result) should
|
* return the line number where the next content (i.e. result) should
|
||||||
* be appended.
|
* be appended.
|
||||||
*
|
|
||||||
* @param int $line
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
protected function addHeaders(
|
private function addHeaders(
|
||||||
Worksheet &$worksheet,
|
Worksheet &$worksheet,
|
||||||
array $globalKeys,
|
array $globalKeys,
|
||||||
$line,
|
int $line,
|
||||||
) {
|
array $cacheDisplayableResult = [],
|
||||||
|
): int {
|
||||||
// get the displayable form of headers
|
// get the displayable form of headers
|
||||||
$displayables = [];
|
$displayables = [];
|
||||||
|
|
||||||
foreach ($globalKeys as $key) {
|
foreach ($globalKeys as $key) {
|
||||||
$displayable = $this->getDisplayableResult($key, '_header');
|
$displayable = $this->getDisplayableResult($key, '_header', $cacheDisplayableResult);
|
||||||
|
|
||||||
if ($displayable instanceof TranslatableInterface) {
|
if ($displayable instanceof TranslatableInterface) {
|
||||||
$displayables[] = $displayable->trans($this->translator, $this->translator->getLocale());
|
$displayables[] = $displayable->trans($this->translator, $this->translator->getLocale());
|
||||||
} else {
|
} else {
|
||||||
$displayables[] = $this->translator->trans($this->getDisplayableResult($key, '_header'));
|
$displayables[] = $this->translator->trans($this->getDisplayableResult($key, '_header', $cacheDisplayableResult));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,9 +244,9 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
* Add the title to the worksheet and merge the cell containing
|
* Add the title to the worksheet and merge the cell containing
|
||||||
* the title.
|
* the title.
|
||||||
*/
|
*/
|
||||||
protected function addTitleToWorkSheet(Worksheet &$worksheet)
|
private function addTitleToWorkSheet(Worksheet &$worksheet, $export)
|
||||||
{
|
{
|
||||||
$worksheet->setCellValue('A1', $this->getTitle());
|
$worksheet->setCellValue('A1', $this->getTitle($export));
|
||||||
$worksheet->mergeCells('A1:G1');
|
$worksheet->mergeCells('A1:G1');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,14 +255,14 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
*
|
*
|
||||||
* @return array where 1st member is spreadsheet, 2nd is worksheet
|
* @return array where 1st member is spreadsheet, 2nd is worksheet
|
||||||
*/
|
*/
|
||||||
protected function createSpreadsheet()
|
private function createSpreadsheet($export)
|
||||||
{
|
{
|
||||||
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
||||||
$worksheet = $spreadsheet->getActiveSheet();
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
// setting the worksheet title and code name
|
// setting the worksheet title and code name
|
||||||
$worksheet
|
$worksheet
|
||||||
->setTitle($this->getTitle())
|
->setTitle($this->getTitle($export))
|
||||||
->setCodeName('result');
|
->setCodeName('result');
|
||||||
|
|
||||||
return [$spreadsheet, $worksheet];
|
return [$spreadsheet, $worksheet];
|
||||||
@ -338,29 +271,38 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
/**
|
/**
|
||||||
* Generate the content and write it to php://temp.
|
* Generate the content and write it to php://temp.
|
||||||
*/
|
*/
|
||||||
protected function generateContent(ExportGenerationContext $context)
|
private function generateContent(
|
||||||
{
|
ExportGenerationContext $context,
|
||||||
[$spreadsheet, $worksheet] = $this->createSpreadsheet();
|
string $tempfile,
|
||||||
|
$result,
|
||||||
|
$formatterData,
|
||||||
|
$export,
|
||||||
|
array $exportData,
|
||||||
|
array $filtersData,
|
||||||
|
array $aggregatorsData,
|
||||||
|
array $cacheDisplayableResult,
|
||||||
|
) {
|
||||||
|
[$spreadsheet, $worksheet] = $this->createSpreadsheet($export);
|
||||||
|
|
||||||
$this->addTitleToWorkSheet($worksheet);
|
$this->addTitleToWorkSheet($worksheet, $export);
|
||||||
$line = $this->addFiltersDescription($worksheet, $context);
|
$line = $this->addFiltersDescription($worksheet, $context, $filtersData);
|
||||||
|
|
||||||
// at this point, we are going to sort retsults for an easier manipulation
|
// at this point, we are going to sort results for an easier manipulation
|
||||||
[$sortedResult, $exportKeys, $aggregatorKeys, $globalKeys] =
|
[$sortedResult, $exportKeys, $aggregatorKeys, $globalKeys] =
|
||||||
$this->sortResult();
|
$this->sortResult($result, $export, $exportData, $aggregatorsData, $formatterData, $cacheDisplayableResult);
|
||||||
|
|
||||||
$line = $this->addHeaders($worksheet, $globalKeys, $line);
|
$line = $this->addHeaders($worksheet, $globalKeys, $line, $cacheDisplayableResult);
|
||||||
|
|
||||||
$line = $this->addContentTable($worksheet, $sortedResult, $line);
|
$this->addContentTable($worksheet, $sortedResult, $line);
|
||||||
|
|
||||||
$writer = match ($this->formatterData['format']) {
|
$writer = match ($formatterData['format']) {
|
||||||
'ods' => \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Ods'),
|
'ods' => \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Ods'),
|
||||||
'xlsx' => \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx'),
|
'xlsx' => \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx'),
|
||||||
'csv' => \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Csv'),
|
'csv' => \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Csv'),
|
||||||
default => throw new \LogicException(),
|
default => throw new \LogicException(),
|
||||||
};
|
};
|
||||||
|
|
||||||
$writer->save($this->tempfile);
|
$writer->save($tempfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -369,7 +311,7 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
*
|
*
|
||||||
* @return string[] an array containing the keys of aggregators
|
* @return string[] an array containing the keys of aggregators
|
||||||
*/
|
*/
|
||||||
protected function getAggregatorKeysSorted()
|
private function getAggregatorKeysSorted(array $aggregatorsData, array $formatterData)
|
||||||
{
|
{
|
||||||
// empty array for aggregators keys
|
// empty array for aggregators keys
|
||||||
$keys = [];
|
$keys = [];
|
||||||
@ -377,7 +319,7 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
// during sorting
|
// during sorting
|
||||||
$aggregatorKeyAssociation = [];
|
$aggregatorKeyAssociation = [];
|
||||||
|
|
||||||
foreach ($this->aggregatorsData as $alias => $data) {
|
foreach ($aggregatorsData as $alias => $data) {
|
||||||
$aggregator = $this->exportManager->getAggregator($alias);
|
$aggregator = $this->exportManager->getAggregator($alias);
|
||||||
$aggregatorsKeys = $aggregator->getQueryKeys($data);
|
$aggregatorsKeys = $aggregator->getQueryKeys($data);
|
||||||
// append the keys from aggregator to the $keys existing array
|
// append the keys from aggregator to the $keys existing array
|
||||||
@ -389,9 +331,9 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sort the result using the form
|
// sort the result using the form
|
||||||
usort($keys, function ($a, $b) use ($aggregatorKeyAssociation) {
|
usort($keys, function ($a, $b) use ($aggregatorKeyAssociation, $formatterData) {
|
||||||
$A = $this->formatterData[$aggregatorKeyAssociation[$a]]['order'];
|
$A = $formatterData[$aggregatorKeyAssociation[$a]]['order'];
|
||||||
$B = $this->formatterData[$aggregatorKeyAssociation[$b]]['order'];
|
$B = $formatterData[$aggregatorKeyAssociation[$b]]['order'];
|
||||||
|
|
||||||
if ($A === $B) {
|
if ($A === $B) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -407,7 +349,7 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
return $keys;
|
return $keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getContentType($format)
|
private function getContentType($format)
|
||||||
{
|
{
|
||||||
switch ($format) {
|
switch ($format) {
|
||||||
case 'csv':
|
case 'csv':
|
||||||
@ -424,23 +366,20 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the displayable result.
|
* Get the displayable result.
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
*/
|
*/
|
||||||
protected function getDisplayableResult($key, mixed $value)
|
private function getDisplayableResult(
|
||||||
{
|
string $key,
|
||||||
if (false === $this->cacheDisplayableResultIsInitialized) {
|
mixed $value,
|
||||||
$this->initializeCache($key);
|
array $cacheDisplayableResult,
|
||||||
}
|
): string|TranslatableInterface|\DateTimeInterface|int|float|bool {
|
||||||
|
|
||||||
$value ??= '';
|
$value ??= '';
|
||||||
|
|
||||||
return \call_user_func($this->cacheDisplayableResult[$key], $value);
|
return \call_user_func($cacheDisplayableResult[$key], $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getTitle(): string
|
private function getTitle($export): string
|
||||||
{
|
{
|
||||||
$original = $this->export->getTitle();
|
$original = $export->getTitle();
|
||||||
|
|
||||||
if ($original instanceof TranslatableInterface) {
|
if ($original instanceof TranslatableInterface) {
|
||||||
$title = $original->trans($this->translator, $this->translator->getLocale());
|
$title = $original->trans($this->translator, $this->translator->getLocale());
|
||||||
@ -455,8 +394,13 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
return $title;
|
return $title;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initializeCache($key)
|
private function initializeDisplayable(
|
||||||
{
|
$result,
|
||||||
|
ExportInterface $export,
|
||||||
|
array $exportData,
|
||||||
|
array $aggregatorsData,
|
||||||
|
): array {
|
||||||
|
$cacheDisplayableResult = [];
|
||||||
/*
|
/*
|
||||||
* this function follows the following steps :
|
* this function follows the following steps :
|
||||||
*
|
*
|
||||||
@ -469,12 +413,11 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
// 1. create an associative array with key and export / aggregator
|
// 1. create an associative array with key and export / aggregator
|
||||||
$keysExportElementAssociation = [];
|
$keysExportElementAssociation = [];
|
||||||
// keys for export
|
// keys for export
|
||||||
foreach ($this->export->getQueryKeys($this->exportData) as $key) {
|
foreach ($export->getQueryKeys($exportData) as $key) {
|
||||||
$keysExportElementAssociation[$key] = [$this->export,
|
$keysExportElementAssociation[$key] = [$export, $exportData];
|
||||||
$this->exportData, ];
|
|
||||||
}
|
}
|
||||||
// keys for aggregator
|
// keys for aggregator
|
||||||
foreach ($this->aggregatorsData as $alias => $data) {
|
foreach ($aggregatorsData as $alias => $data) {
|
||||||
$aggregator = $this->getExportManager()->getAggregator($alias);
|
$aggregator = $this->getExportManager()->getAggregator($alias);
|
||||||
|
|
||||||
foreach ($aggregator->getQueryKeys($data) as $key) {
|
foreach ($aggregator->getQueryKeys($data) as $key) {
|
||||||
@ -487,7 +430,7 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
|
|
||||||
$allValues = [];
|
$allValues = [];
|
||||||
// store all the values in an array
|
// store all the values in an array
|
||||||
foreach ($this->result as $row) {
|
foreach ($result as $row) {
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$allValues[$key][] = $row[$key];
|
$allValues[$key][] = $row[$key];
|
||||||
}
|
}
|
||||||
@ -498,15 +441,14 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
foreach ($keysExportElementAssociation as $key => [$element, $data]) {
|
foreach ($keysExportElementAssociation as $key => [$element, $data]) {
|
||||||
// handle the case when there is not results lines (query is empty)
|
// handle the case when there is not results lines (query is empty)
|
||||||
if ([] === $allValues) {
|
if ([] === $allValues) {
|
||||||
$this->cacheDisplayableResult[$key] = $element->getLabels($key, ['_header'], $data);
|
$cacheDisplayableResult[$key] = $element->getLabels($key, ['_header'], $data);
|
||||||
} else {
|
} else {
|
||||||
$this->cacheDisplayableResult[$key] =
|
$cacheDisplayableResult[$key] =
|
||||||
$element->getLabels($key, \array_unique($allValues[$key]), $data);
|
$element->getLabels($key, \array_unique($allValues[$key]), $data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the cache is initialized !
|
return $cacheDisplayableResult;
|
||||||
$this->cacheDisplayableResultIsInitialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -544,23 +486,28 @@ class SpreadSheetFormatter implements FormatterInterface, ExportManagerAwareInte
|
|||||||
* )
|
* )
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected function sortResult()
|
private function sortResult(
|
||||||
{
|
$result,
|
||||||
|
ExportInterface $export,
|
||||||
|
array $exportData,
|
||||||
|
array $aggregatorsData,
|
||||||
|
array $formatterData,
|
||||||
|
array $cacheDisplayableResult,
|
||||||
|
) {
|
||||||
// get the keys for each row
|
// get the keys for each row
|
||||||
$exportKeys = $this->export->getQueryKeys($this->exportData);
|
$exportKeys = $export->getQueryKeys($exportData);
|
||||||
$aggregatorKeys = $this->getAggregatorKeysSorted();
|
$aggregatorKeys = $this->getAggregatorKeysSorted($aggregatorsData, $formatterData);
|
||||||
|
|
||||||
$globalKeys = \array_merge($aggregatorKeys, $exportKeys);
|
$globalKeys = \array_merge($aggregatorKeys, $exportKeys);
|
||||||
|
|
||||||
$sortedResult = \array_map(function ($row) use ($globalKeys) {
|
$sortedResult = \array_map(function ($row) use ($globalKeys, $cacheDisplayableResult) {
|
||||||
$newRow = [];
|
$newRow = [];
|
||||||
|
|
||||||
foreach ($globalKeys as $key) {
|
foreach ($globalKeys as $key) {
|
||||||
$newRow[] = $this->getDisplayableResult($key, $row[$key]);
|
$newRow[] = $this->getDisplayableResult($key, $row[$key], $cacheDisplayableResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $newRow;
|
return $newRow;
|
||||||
}, $this->result);
|
}, $result);
|
||||||
|
|
||||||
\array_multisort($sortedResult);
|
\array_multisort($sortedResult);
|
||||||
|
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Tests\Export\Formatter;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Export\ExportGenerationContext;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\ExportManager;
|
||||||
|
use Chill\MainBundle\Export\Formatter\SpreadSheetFormatter;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Component\Translation\TranslatableMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class SpreadsheetFormatterTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
public function testGenerate(): void
|
||||||
|
{
|
||||||
|
$translator = $this->prophesize(\Symfony\Contracts\Translation\TranslatorInterface::class);
|
||||||
|
$translator->getLocale()->willReturn('en');
|
||||||
|
$exportManager = $this->prophesize(ExportManager::class);
|
||||||
|
|
||||||
|
$result =
|
||||||
|
[
|
||||||
|
['export_count_activity' => 1, 'person_age' => 65, 'aggregator_some' => 'label0'], // row 0
|
||||||
|
];
|
||||||
|
$exportAlias = 'count_activity_linked_to_person';
|
||||||
|
$formatterData =
|
||||||
|
['format' => 'xlsx', 'person_age_aggregator' => ['order' => 1], 'aggregator2' => ['order' => 2]];
|
||||||
|
$exportData = [];
|
||||||
|
$filtersData =
|
||||||
|
[
|
||||||
|
'person_age_filter' => ['min_age' => 18, 'max_age' => 120, 'date_calc' => new RollingDate(RollingDate::T_TODAY)],
|
||||||
|
'filter2' => [],
|
||||||
|
];
|
||||||
|
$aggregatorsData =
|
||||||
|
[
|
||||||
|
'person_age_aggregator' => ['date_age_calculation' => new RollingDate(RollingDate::T_TODAY)],
|
||||||
|
'aggregator2' => [],
|
||||||
|
];
|
||||||
|
$context =
|
||||||
|
new ExportGenerationContext($user = new User());
|
||||||
|
|
||||||
|
$export = $this->prophesize(ExportInterface::class);
|
||||||
|
$export->getTitle()->willReturn('Count activity linked to person');
|
||||||
|
$translator->trans('Count activity linked to person')->willReturn('Count activity linked to person');
|
||||||
|
$export->getQueryKeys($exportData)->willReturn(['export_count_activity']);
|
||||||
|
$export->getLabels('export_count_activity', [1], $exportData)
|
||||||
|
->willReturn(fn (int|string $value): int|string => '_header' === $value ? 'Count activities' : $value);
|
||||||
|
$translator->trans('Count activities')->willReturn('Count activities');
|
||||||
|
$exportManager->getExport($exportAlias)->willReturn($export->reveal());
|
||||||
|
|
||||||
|
$aggregator = $this->prophesize(\Chill\MainBundle\Export\AggregatorInterface::class);
|
||||||
|
$aggregator->getTitle()->willReturn('Person age');
|
||||||
|
$aggregator->getQueryKeys($aggregatorsData['person_age_aggregator'])->willReturn(['person_age']);
|
||||||
|
$aggregator->getLabels('person_age', [65], $aggregatorsData['person_age_aggregator'])
|
||||||
|
->willReturn(fn (int|string $value): int|string => '_header' === $value ? 'Group by age' : $value);
|
||||||
|
$translator->trans('Group by age')->willReturn('Group by age');
|
||||||
|
$exportManager->getAggregator('person_age_aggregator')->willReturn($aggregator->reveal());
|
||||||
|
|
||||||
|
$aggregator2 = $this->prophesize(\Chill\MainBundle\Export\AggregatorInterface::class);
|
||||||
|
$aggregator2->getTitle()->willReturn(new TranslatableMessage('Some'));
|
||||||
|
$aggregator2->getQueryKeys($aggregatorsData['aggregator2'])->willReturn(['aggregator_some']);
|
||||||
|
$aggregator2->getLabels('aggregator_some', ['label0'], $aggregatorsData['aggregator2'])
|
||||||
|
->willReturn(fn (int|string $value): TranslatableMessage => new TranslatableMessage('_header' === $value ? 'Aggregator 2 header' : $value));
|
||||||
|
$translator->trans('Aggregator 2 header', [], null, 'en')->willReturn('Aggregator 2 header');
|
||||||
|
$translator->trans('label0', [], null, 'en')->willReturn('label0');
|
||||||
|
$exportManager->getAggregator('aggregator2')->willReturn($aggregator2->reveal());
|
||||||
|
|
||||||
|
$filter = $this->prophesize(\Chill\MainBundle\Export\FilterInterface::class);
|
||||||
|
$filter->getTitle()->willReturn('Person by age');
|
||||||
|
$filter->describeAction($filtersData['person_age_filter'], $context)
|
||||||
|
->willReturn(['Filter by age, from {{ start }} to {{ end }}', ['{{ start }}' => '18', '{{ end }}' => '120']]);
|
||||||
|
$translator->trans('Filter by age, from {{ start }} to {{ end }}', ['{{ start }}' => '18', '{{ end }}' => '120'])
|
||||||
|
->willReturn('Filter by age, from 18 to 120');
|
||||||
|
$exportManager->getFilter('person_age_filter')->willReturn($filter->reveal());
|
||||||
|
|
||||||
|
$filter2 = $this->prophesize(\Chill\MainBundle\Export\FilterInterface::class);
|
||||||
|
$filter2->getTitle()->willReturn(new TranslatableMessage('Some other filter'));
|
||||||
|
$filter2->describeAction($filtersData['filter2'], $context)
|
||||||
|
->willReturn(new TranslatableMessage('Other filter description'));
|
||||||
|
$translator->trans('Other filter description', [], null, 'en')
|
||||||
|
->willReturn('Some other filter description');
|
||||||
|
$exportManager->getFilter('filter2')->willReturn($filter2->reveal());
|
||||||
|
|
||||||
|
|
||||||
|
// create the formatter
|
||||||
|
$formatter = new SpreadSheetFormatter($translator->reveal());
|
||||||
|
$formatter->setExportManager($exportManager->reveal());
|
||||||
|
|
||||||
|
$result = $formatter->generate(
|
||||||
|
$result,
|
||||||
|
$formatterData,
|
||||||
|
$exportAlias,
|
||||||
|
$exportData,
|
||||||
|
$filtersData,
|
||||||
|
$aggregatorsData,
|
||||||
|
$context,
|
||||||
|
);
|
||||||
|
|
||||||
|
$tempFile = tempnam(sys_get_temp_dir(), 'test_spreadsheet_formatter_');
|
||||||
|
file_put_contents($tempFile, $result->content);
|
||||||
|
$spreadsheet = IOFactory::load($tempFile);
|
||||||
|
$cells = $spreadsheet->getActiveSheet()->rangeToArray(
|
||||||
|
'A1:G6',
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
unlink($tempFile);
|
||||||
|
|
||||||
|
self::assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $result->contentType);
|
||||||
|
self::assertEquals($cells[1], ['A' => 'Count activity linked to perso…', 'B' => null, 'C' => null, 'D' => null, 'E' => null, 'F' => null, 'G' => null]);
|
||||||
|
self::assertEquals($cells[2], ['A' => null, 'B' => null, 'C' => null, 'D' => null, 'E' => null, 'F' => null, 'G' => null]);
|
||||||
|
self::assertEquals($cells[3], ['A' => 'Filter by age, from 18 to 120', 'B' => null, 'C' => null, 'D' => null, 'E' => null, 'F' => null, 'G' => null]);
|
||||||
|
self::assertEquals($cells[4], ['A' => 'Some other filter description', 'B' => null, 'C' => null, 'D' => null, 'E' => null, 'F' => null, 'G' => null]);
|
||||||
|
self::assertEquals($cells[5], ['A' => 'Group by age', 'B' => 'Aggregator 2 header', 'C' => 'Count activities', 'D' => null, 'E' => null, 'F' => null, 'G' => null]);
|
||||||
|
self::assertEquals($cells[6], ['A' => 65, 'B' => 'label0', 'C' => 1, 'D' => null, 'E' => null, 'F' => null, 'G' => null]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user