Aquí está la cadena de traducción que necesitas traducir al idioma del local 'es': ```

Las interfaces predeterminadas de Sonata son CRUDs. Esto es increíblemente práctico (de lo contrario, no lo usaríamos). Pero una administración no solo está compuesta de CRUDs.
Aquí veremos cómo crear una página simple de exportación de datos, eliminando las vistas predeterminadas de las interfaces y creando las nuestras para gestionar nuestro botón de exportación.

Sélection_239-1

1 – Añadiendo la biblioteca de exportación

composer require sonata-project/exporter

Luego necesitamos añadir un archivo de configuración para nuestro exportador.

#config/packages/sonata_exporter.yml
sonata_exporter:
  writers:
    csv:
      delimiter: ";"

2 – Creando nuestro controlador

Vamos a crear nuestro controlador que contendrá nuestra configuración básica.

<?php
namespace App\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface;
use Sonata\FormatterBundle\Form\Type\SimpleFormatterType;
use Knp\Menu\ItemInterface as MenuItemInterface;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\RouteCollection;



class ExportModuleAdmin extends AbstractAdmin
{
    
    public function __construct( $code, $class, $baseControllerName ) {
        parent::__construct( $code, $class, $baseControllerName );
        

    }
    
    public function configure()
    {
        parent::configure();
    }
}

Luego necesitamos crear nuestras acciones:
Eliminamos todas nuestras rutas, creamos una ruta de módulo de exportación para nuestra página.

    protected function configureRoutes(RouteCollection $collection)
    {
        $collection->clearExcept(['export-module']);
        $collection->remove('create');
        $collection->add('export-module');
    }

Luego necesitamos registrar nuestra interfaz en los servicios

    admin.export:
        class: App\Admin\ExportModuleAdmin
        arguments: [~, App\Entity\Export, App\Admin\CustomAction]
        tags:
            - { name: sonata.admin, manager_type: orm, label: "Export CSV" , label_translator_strategy: sonata.admin.label.strategy.underscore, label_catalogue: default}
        public: true

Como puedes notar nuestro tercer argumento es un controlador de admin adicional, que gestionará nuestras acciones (src/Admin/CustomAction.php).

Y es directamente en la acción donde definiremos la vista que usaremos para añadir nuestro botón.
Así que creamos un método exportModuleAction() que contiene nuestra referencia a nuestra plantilla.

<?php

namespace App\Admin;

use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\Query\ResultSetMapping;

class CustomAction extends CRUDController
{
    public function exportDelefAction(){
        return $this->renderWithExtraParams('Admin/export.html.twig', array(
            'action' => 'export',
            'elements' => $this->admin->getShow(),
        ), null);
    }
}

Y nuestro archivo twig que se encuentra en esta jerarquía templates/Admin/export.html.twig contiene nuestro código, muy simple, con un enlace para recuperar la exportación.
El enlace es justo la ruta de exportación de una interfaz de admin CRUD que contiene como argumento el formato deseado (csv).
La función de exportación es una función automática de las interfaces CRUD.

{% extends '@SonataAdmin/standard_layout.html.twig' %}
{% block sonata_admin_content %}
{% include 'SonataCoreBundle:FlashMessage:render.html.twig' %}

<div>
    <h2 class="title-border">Export CSV</h2>

    <div class="box box-primary">
        <div class="box-body">
            <ul class="menu list-unstyled mb-0">
                <li><a class="btn-link" href="{{ path('admin_app_wdeclar_export', {'format' : 'csv'}) }}">Cliquez ici pour télécharger le fichier csv</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

Para hacer nuestra acción directamente disponible desde el tablero, necesitamos registrar la acción en el archivo sonata_admin.yml

Y para verificar la ruta que proporcionaremos, simplemente necesitamos usar el comando:

php bin/console debug:router

Una vez identificado, lo usamos como la URL de acceso para nuestro botón de exportación en el menú.

#config/packages/sonata_admin.yaml
sonata_admin:
    title: 'Sonata Admin'
    dashboard:
        blocks:
            - { type: sonata.admin.block.admin_list, position: left }
        groups:
            delef.admin.group.contrats:
                label: Gestion des contrats
                icon: '<i class="fa fa-cogs "></i>'
                items:
                    - route: admin_app_export_export-module
                      label:  "Export CSV"

Pero eso no es todo. También necesitamos configurar nuestro botón en el tablero. Vamos a añadir el método getDashboardActions().
Añadimos nuestra acción y la clase CSS del icono que queremos usar. Aquí usamos un icono de Font Awesome 5 que hemos añadido a nuestro proyecto.

public function getDashboardActions()
    {
        $actions = parent::getDashboardActions();
    
        $actions['import'] = [
            'label'              => 'Export',
            'url'                => $this->generateUrl('export-delef'),
            'icon'               => ' fas fa-file-export',
            'translation_domain' => 'SonataAdminBundle', // optional
            'template'           => '@SonataAdmin/CRUD/dashboard__action.html.twig', // optional
        ];
    
        return $actions;
    }

Esto nos da el siguiente archivo PHP:

<?php
namespace App\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface;
use Sonata\FormatterBundle\Form\Type\SimpleFormatterType;
use Knp\Menu\ItemInterface as MenuItemInterface;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\RouteCollection;


class ExportModuleAdmin extends AbstractAdmin
{
    
    public function __construct( $code, $class, $baseControllerName ) {
        parent::__construct( $code, $class, $baseControllerName );
        

    }
    
    public function configure()
    {
        parent::configure();
    }
    
    
    protected function configureRoutes(RouteCollection $collection)
    {
        //$collection->clearExcept(['list']);
        $collection->clearExcept(['export-delef']);
        $collection->remove('create');
        $collection->add('export-delef');
    }
    

    public function setContainer(ContainerInterface $container)
    {
        $this->container = $container;
    }
    
   
    public function getDashboardActions()
    {
        $actions = parent::getDashboardActions();
    
        $actions['import'] = [
            'label'              => 'Export',
            'url'                => $this->generateUrl('export-delef'),
            'icon'               => ' fas fa-file-export',
            'translation_domain' => 'SonataAdminBundle', // optional
            'template'           => '@SonataAdmin/CRUD/dashboard__action.html.twig', // optional
        ];
    
        return $actions;
    }
    
}

En esta etapa, tenemos nuestro acceso desde el tablero:

Sélection_238

Y nuestra página de descarga:

Sélection_239

3 – Configuración de la exportación

Solo nos falta la configuración de nuestra exportación.
Vimos anteriormente que hacemos referencia a una acción de 'exportación' predeterminada de una interfaz CRUD de nuestro admin.
Así que es directamente en nuestra entidad donde vamos a configurar la exportación.

En nuestro CRUD añadimos la siguiente referencia:

use App\Source\DBALStatementSourceIterator;

Y los dos métodos siguientes:

    public function getDataSourceIterator()
    {
        $container = $this->getConfigurationPool()->getContainer();
        $em = $container->get('doctrine.orm.entity_manager');
        $conn = $em->getConnection();
        $fields = $this->getExportFields();
        $field_str = implode(',', $fields);
        $sql = "SELECT {$field_str} FROM myTable d where champs1 ='valeur' order by id asc";
        $stmt = $conn->prepare($sql);
        $stmt->execute();

        return new DBALStatementSourceIterator($stmt);
    }

    public function getExportFields() {
        return [
            'd.champs1','d.champs2','d.champs3'
        ];
    }

Solo tenemos que añadir nuestro iterador fuente que se encuentra en src/Source/DBALStatementSourceIterator.php

<?php

namespace App\Source;

use Sonata\Exporter\Exception\InvalidMethodCallException;
use Sonata\Exporter\Source\SourceIteratorInterface;

class DBALStatementSourceIterator implements SourceIteratorInterface
{
    /**
     * @var \Doctrine\DBAL\Statement
     */
    protected $statement;

    /**
     * @var mixed
     */
    protected $current;

    /**
     * @var int
     */
    protected $position;

    /**
     * @var bool
     */
    protected $rewinded;

    /**
     * @param \Doctrine\DBAL\Statement $statement
     */
    public function __construct(\Doctrine\DBAL\Statement $statement)
    {
        $this->statement = $statement;
        $this->position = 0;
        $this->rewinded = false;
    }

    /**
     * {@inheritdoc}
     */
    public function current()
    {
        return $this->current;
    }

    /**
     * {@inheritdoc}
     */
    public function next()
    {
        $this->current = $this->statement->fetch(\Doctrine\DBAL\FetchMode::ASSOCIATIVE);
        ++$this->position;
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * {@inheritdoc}
     */
    public function valid()
    {
        return \is_array($this->current);
    }

    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        if ($this->rewinded) {
            throw new InvalidMethodCallException('Cannot rewind a PDOStatement');
        }

        $this->current = $this->statement->fetch(\Doctrine\DBAL\FetchMode::ASSOCIATIVE);
        $this->rewinded = true;
    }
}

¡Y eso es todo!

```