Saltar al contenido
Marcos Peña
Menú
  • Acerca de…
Menú

Synfony 4: exporter personalizado

Publicada el noviembre 4, 2020noviembre 6, 2020 por Marcos

Escenario

El bunde de SonataAdmin para Symfony te permite crear listados exportables sin apenas esfuerzo. Por defecto, ofrece la posibilidad de exportar a csv, xls, json y xml. Basta con crear el fichero Admin correspondiente a la entidad. Por ejemplo si quisiéramos que un listado de facturas de nuestra aplicación pueda ser exportado a Excel, deberemos crear primero la clase de tipo Admin, InvoiceAdmin, en la que sobreescriberemos dos métodos extendidos de la clase AbstractAdmin. getExportFormats() para indicar qué formatos queremos que estén disponibles en el listado para exportar y getExportFields() en el que definiremos los campos que queremos que figuren el fichero exportado.
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
 
// src/Admin/InvoiceAdmin.php
 
namespace Acme\Admin;
 
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\DoctrineORMAdminBundle\Filter\DateRangeFilter;
use Sonata\Form\Type\DateTimeRangePickerType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
 
/**
*
* @author Marcos
*/
class InvoiceAdmin extends AbstractAdmin
{
...
    /**
     * @return array
     */
    public function getExportFormats()
    {
        return [
            'xls',
        ];
    }
 
    /**
     * @return array
     */
    public function getExportFields()
    {
        return array(
            $this->trans('reference') => 'reference',
            $this->trans('customer') => 'customer',
            $this->trans('amount') => 'amount',
            $this->trans('issueDate') => 'issueDate'
        ...
        );
    }
}
...

Problema

Los campos de fecha e importe no vienen formateados cómo quiere el cliente. Podría crear dos métodos adicionales en la entidad Invoice que devolvieran el campo transformado. Por ejemplo:
PHP
1
2
3
4
    public function getIssueDateFormatted(): ?string
    {
        return $this->issueDate !== null ? $this->issueDate->format("d/m/Y") : null;
    }        
Y, en el fichero de InvoiceAdmin:
PHP
1
2
3
4
5
6
7
8
9
10
11
    /**
     * @return array
     */
    public function getExportFields()
    {
        return array(
            ...
            $this->trans('issueDate') => 'issueDateFormatted'
            ...
        );
    }
Es una solución, y de hecho la he empleado para los campos de fecha, pero para el de importe no conseguía que se mostrara correctamente en el fichero Excel exportado, cuando en el listado HTML sí aparecía correctamente.

Solución

Primero damos de alta un Writer para un nuevo tipo de fichero (xlsx) en la aplicación. La clase ha de implementar los métodos de la interfaz TypedWriterInterface. A continuación registramos el servicio en services.yaml y a correr. Para el writer he utilizado la biblioteca PhpOffice\PhpSpreadsheet y me he inspirado en un gist algo desfasado, pero que me ha puesto sobre la pista:
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<?php
 
namespace Acme\Export;
 
use Sonata\Exporter\Writer\TypedWriterInterface;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
 
/**
* Description of XlsxWriter
*
* @author marcos
*/
class XlsxWriter implements TypedWriterInterface
{
 
    const LABEL_COLUMN = 1;
 
    /** @var  Spreadsheet */
    private $phpExcelObject;
    
    /** @var array */
    private $headerColumns = [];
 
    /** @var  string */
    private $filename;
 
    /** @var int */
    protected $position;
 
    public function __construct(string $filename)
    {
        $this->filename = $filename;
        $this->position = 2;
    }
 
    public function getDefaultMimeType(): string
    {
        return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
    }
 
    public function getFormat(): string
    {
        return 'xlsx';
    }
 
    /**
     * Create PHPExcel object and set defaults
     */
    public function open()
    {
        $this->phpExcelObject = new Spreadsheet();
    }
 
    /**
     * {@inheritdoc}
     */
    public function write(array $data)
    {
        $this->init($data);
        foreach ($data as $header => $value) {
            $this->setCellValue($this->getColumn($header), $value);
        }
        ++$this->position;
    }
 
    /**
     *  Set labels
     * @param $data
     *
     * @return void
     */
    protected function init($data)
    {
        if ($this->position > 2) {
            return;
        }
        $i = 0;
        foreach ($data as $header => $value) {
            $column = self::formatColumnName($i);
            $this->setHeader($column, $header);
            $i++;
        }
        $this->setBoldLabels();
    }
 
    /**
     * Save Excel file
     */
    public function close()
    {
        $writer = IOFactory::createWriter($this->phpExcelObject, 'Xlsx');
        $writer->save($this->filename);
    }
 
    /**
     * Returns letter for number based on Excel columns
     * @param int $number
     * @return string
     */
    public static function formatColumnName($number)
    {
        for ($char = ""; $number >= 0; $number = intval($number / 26) - 1) {
            $char = chr($number % 26 + 0x41) . $char;
        }
        return $char;
    }
 
    /**
     * @return \PhpOffice\PhpSpreadsheet\Spreadsheet
     */
    private function getActiveSheet()
    {
        return $this->phpExcelObject->getActiveSheet();
    }
 
    /**
     * Makes header bold
     */
    private function setBoldLabels()
    {
        $this->getActiveSheet()->getStyle(
                sprintf(
                        "%s1:%s1",
                        reset($this->headerColumns),
                        end($this->headerColumns)
                )
        )->getFont()->setBold(true);
    }
 
    /**
     * Sets cell value
     * @param string $column
     * @param string $value
     */
    private function setCellValue($column, $value)
    {
        $this->getActiveSheet()->setCellValue($column, $value);
    }
 
    /**
     * Set column label and make column auto size
     * @param string $column
     * @param string $value
     */
    private function setHeader($column, $value)
    {
        $this->setCellValue($column . self::LABEL_COLUMN, $value);
        $this->getActiveSheet()->getColumnDimension($column)->setAutoSize(true);
        $this->headerColumns[$value] = $column;
    }
 
    /**
     * Get column name
     * @param string $name
     * @return string
     */
    private function getColumn($name)
    {
        return $this->headerColumns[$name] . $this->position;
    }
 
}
Añadimos el siguiente bloque en el fichero de configuracón de servicios services.yaml…
YAML
1
2
3
4
5
    gestpark.exporter.writer.xlsx:
        class: GestPark\Export\XlsxWriter        
        arguments: ['%sonata.exporter.writer.xls.filename%']
        tags:
            - { name: sonata.exporter.writer }
El nuevo formato en el fichero de InvoiceAdmin…
PHP
1
2
3
4
5
6
7
8
9
    /**
     * @return array
     */
    public function getExportFormats()
    {
        return [
            'xlsx',
        ];
    }
Y, por último, en el fichero translation/SonataAdminBundle.es.xlf incluimos el siguiente bloque de traducción:
XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" datatype="plaintext" original="file.ext">
        <body>
            ...
            <trans-unit id="export_format_xlsx">
                <source>export_format_xlsx</source>
                <target>xlsx</target>
            </trans-unit>  
            ...
        </body>
    </file>
</xliff>
Nota: Estoy usando la versión de Symfony 4.4 y la 3.78.1 de SonataAdmin. Es posible que, para que te funcione, tengas que instalar el bundle de sonata Exporter.

Deja una respuesta Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Artículos

  • abril 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • junio 2021
  • mayo 2021
  • abril 2021
  • marzo 2021
  • febrero 2021
  • enero 2021
  • diciembre 2020
  • noviembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • julio 2020
  • junio 2020
  • mayo 2020
  • abril 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • diciembre 2019
  • noviembre 2019
  • octubre 2019
  • septiembre 2019
  • agosto 2019
  • julio 2019
  • junio 2019
  • mayo 2019
  • abril 2019
  • marzo 2019
  • febrero 2019
  • enero 2019
  • noviembre 2018
  • octubre 2018
  • julio 2016
  • mayo 2016
  • abril 2016
  • agosto 2015
  • febrero 2014
  • junio 2013
  • febrero 2013
  • noviembre 2012
  • octubre 2012
  • septiembre 2012
  • abril 2012
  • enero 2012

Categorías

  • Cómo
  • Consejos
  • Hacks
  • Sin sentido
  • Tutorial

Etiquetas

apache API aws cache Centos Codeigniter EAC firefox flash freebsd freenas gmail Google guacamole java Javascript jQuery kubuntu Linux mysql nas4free nginx php plesk postfix python react rtorrent Seguridad sftp sonata-admin spring spring-boot sql ssh ssl symfony symfony2 symfony4 thymeleaf ubuntu vnc wget windows wine

Entradas recientes

  • Confirmación antes de apagar
  • cURL error 60: SSL certificate problem: unable to get local issuer certificate
  • Oracle JDK 7 is NOT installed.
  • ssh: connect to host: Connection refused
  • Restringir acceso a un bucket de AWS s3 por IP

Comentarios recientes

  • Marcos en Deobbing flash
  • Irosales en Deobbing flash
  • Instalación certificado SSL de un vendedor – Marcos Peña en Instala un certificado SSL en tres pasos
  • gerMdz en Cómo instalar un certificado letsencrypt para un sitio gestionado por Cloudflare
  • Jose Olalla en Cómo ampliar el espacio en disco en 1and1

Archivos

  • abril 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • junio 2021
  • mayo 2021
  • abril 2021
  • marzo 2021
  • febrero 2021
  • enero 2021
  • diciembre 2020
  • noviembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • julio 2020
  • junio 2020
  • mayo 2020
  • abril 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • diciembre 2019
  • noviembre 2019
  • octubre 2019
  • septiembre 2019
  • agosto 2019
  • julio 2019
  • junio 2019
  • mayo 2019
  • abril 2019
  • marzo 2019
  • febrero 2019
  • enero 2019
  • noviembre 2018
  • octubre 2018
  • julio 2016
  • mayo 2016
  • abril 2016
  • agosto 2015
  • febrero 2014
  • junio 2013
  • febrero 2013
  • noviembre 2012
  • octubre 2012
  • septiembre 2012
  • abril 2012
  • enero 2012

Categorías

  • Cómo
  • Consejos
  • Hacks
  • Sin sentido
  • Tutorial

Meta

  • Acceder
  • Feed de entradas
  • Feed de comentarios
  • WordPress.org
© 2022 Marcos Peña | Funciona con Minimalist Blog Tema para WordPress