* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ namespace Chill\MainBundle\Export\Formatter; use Symfony\Component\HttpFoundation\Response; use Chill\MainBundle\Export\FormatterInterface; use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Form\FormBuilderInterface; use Chill\MainBundle\Export\ExportManager; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; // command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff /** * Create a CSV List for the export * * @author Champs-Libres */ class SpreadsheetListFormatter implements FormatterInterface { /** * This variable cache the labels internally * * @var string[] */ protected $labelsCache = null; protected $result = null; protected $exportAlias = null; protected $exportData = null; protected $formatterData = null; /** * * @var ExportManager */ protected $exportManager; /** * * @var TranslatorInterface */ protected $translator; public function __construct(TranslatorInterface $translatorInterface, ExportManager $exportManager) { $this->translator = $translatorInterface; $this->exportManager = $exportManager; } public function getType() { return FormatterInterface::TYPE_LIST; } /** * build a form, which will be used to collect data required for the execution * of this formatter. * * @uses appendAggregatorForm * @param FormBuilderInterface $builder * @param type $exportAlias * @param array $aggregatorAliases */ public function buildForm( FormBuilderInterface $builder, $exportAlias, array $aggregatorAliases ){ $builder ->add('format', ChoiceType::class, array( 'choices' => array( 'OpenDocument Format (.ods) (LibreOffice, ...)' => 'ods', 'Microsoft Excel 2007-2013 XML (.xlsx) (Microsoft Excel, LibreOffice)' => 'xlsx' ), 'placeholder' => 'Choose the format' )) ->add('numerotation', ChoiceType::class, array( 'choices' => array( 'yes' => true, 'no' => false ), 'expanded' => true, 'multiple' => false, 'label' => "Add a number on first column", 'data' => true )); } public function getName() { return 'Spreadsheet list formatter (.xlsx, .ods)'; } /** * Generate a response from the data collected on differents ExportElementInterface * * @param mixed[] $result The result, as given by the ExportInterface * @param mixed[] $formatterData collected from the current form * @param string $exportAlias the id of the current export * @param array $filtersData an array containing the filters data. The key are the filters id, and the value are the data * @param array $aggregatorsData an array containing the aggregators data. The key are the filters id, and the value are the data * @return \Symfony\Component\HttpFoundation\Response The response to be shown */ public function getResponse( $result, $formatterData, $exportAlias, array $exportData, array $filtersData, array $aggregatorsData ) { $this->result = $result; $this->exportAlias = $exportAlias; $this->exportData = $exportData; $this->formatterData = $formatterData; $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $this->prepareHeaders($worksheet); $i = 1; foreach ($result as $row) { $line = array(); if ($this->formatterData['numerotation'] === true) { $worksheet->setCellValue('A'.($i+1), (string) $i); } $a = $this->formatterData['numerotation'] ? 'B' : 'A'; foreach ($row as $key => $value) { $row = $a.($i+1); if ($value instanceof \DateTimeInterface) { $worksheet->setCellValue($row, Date::PHPToExcel($value)); $worksheet->getStyle($row) ->getNumberFormat() ->setFormatCode(NumberFormat::FORMAT_DATE_DDMMYYYY); } else { $worksheet->setCellValue($row, $this->getLabel($key, $value)); } $a ++; } $i++; } switch ($this->formatterData['format']) { case 'ods': $writer = \PhpOffice\PhpSpreadsheet\IOFactory ::createWriter($spreadsheet, 'Ods'); $contentType = "application/vnd.oasis.opendocument.spreadsheet"; break; case 'xlsx': $writer = \PhpOffice\PhpSpreadsheet\IOFactory ::createWriter($spreadsheet, 'Xlsx'); $contentType = 'application/vnd.openxmlformats-officedocument.' . 'spreadsheetml.sheet'; break; case 'csv': $writer = \PhpOffice\PhpSpreadsheet\IOFactory ::createWriter($spreadsheet, 'Csv'); $contentType = 'text/csv'; break; default: // this should not happen // throw an exception to ensure that the error is catched throw new \OutOfBoundsException("The format ".$this->formatterData['format']. " is not supported"); } $response = new Response(); $response->headers->set('content-type', $contentType); $tempfile = \tempnam(\sys_get_temp_dir(), ''); $writer->save($tempfile); $f = \fopen($tempfile, 'r'); $response->setContent(\stream_get_contents($f)); fclose($f); // remove the temp file from disk \unlink($tempfile); return $response; } /** * add the headers to the csv file * * @param Worksheet $worksheet */ protected function prepareHeaders(Worksheet $worksheet) { $keys = $this->exportManager->getExport($this->exportAlias)->getQueryKeys($this->exportData); // we want to keep the order of the first row. So we will iterate on the first row of the results $first_row = count($this->result) > 0 ? $this->result[0] : array(); $header_line = array(); if ($this->formatterData['numerotation'] === true) { $header_line[] = $this->translator->trans('Number'); } foreach ($first_row as $key => $value) { $header_line[] = $this->translator->trans( $this->getLabel($key, '_header')); } if (count($header_line) > 0) { $worksheet->fromArray($header_line, NULL, 'A1'); } } /** * Give the label corresponding to the given key and value. * * @param string $key * @param string $value * @return string * @throws \LogicException if the label is not found */ protected function getLabel($key, $value) { if ($this->labelsCache === null) { $this->prepareCacheLabels(); } if (!\array_key_exists($key, $this->labelsCache)){ throw new \OutOfBoundsException(sprintf("The key \"%s\" " . "is not present in the list of keys handled by " . "this query. Check your `getKeys` and `getLabels` " . "methods. Available keys are %s.", $key, \implode(", ", \array_keys($this->labelsCache)))); } return $this->labelsCache[$key]($value); } /** * Prepare the label cache which will be used by getLabel. This function * should be called only once in the generation lifecycle. */ protected function prepareCacheLabels() { $export = $this->exportManager->getExport($this->exportAlias); $keys = $export->getQueryKeys($this->exportData); foreach($keys as $key) { // get an array with all values for this key if possible $values = \array_map(function ($v) use ($key) { return $v[$key]; }, $this->result); // store the label in the labelsCache property $this->labelsCache[$key] = $export->getLabels($key, $values, $this->exportData); } } }