symfonyeasyadmin

Symfony EasyAdminBundle: Creating a custom crud action to save a csv


I am following this tutorial here. I would like to create a custom action that saves a csv file. I want to save my user class, but for now I just want to understand why the function saveUsersToCsv is not being called. Here is my UserCrudController:

UserCrudController.php

<?php

namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\ArrayField;

class UserCrudController extends AbstractCrudController
{
    public static function getEntityFqcn(): string
    {
        return User::class;
    }

    // configurable fields
    // see: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
    public function configureFields(string $pageName): iterable
    {
        return [
            TextField::new("username"),
            EmailField::new('email'),
            ArrayField::new('roles'),
        ];
    }
    public function configureActions(Actions $actions): Actions
    {
        $saveCsv = Action::new('saveCsv','Save as Csv', 'fa fa-save-as-csv')
            ->displayAsButton()
            ->linkToCrudAction('saveUsersToCsv');

        return $actions
            ->add(Crud::PAGE_DETAIL,$saveCsv);
    }
    // https://stackoverflow.com/questions/27888374/create-csv-and-force-download-of-file
    public function saveUsersToCsv(AdminContext $context){
        // $instance = $context->getEntity()->getInstance();

        $fileName = "test";
        $filePath = $_SERVER["DOCUMENT_ROOT"] . $fileName . '.csv';
        $output = fopen($filePath,'w+');
        fputcsv($output,array("Number","Description","test"));
        fputcsv($output,array("100","TestDescription","10"));

        // set the headers
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename ="'.$fileName.".csv'");
        header('Content-Length: ' .filesize($filePath));
        echo readfile($filePath);
    }
}

The button for saving a user does correctly show up, but when clicked nothing happens:

enter image description here

How can I get the button click to call the function to save the csv file?


Solution

  • I have found a solution. Here is the code in UserCrudController.php.

    <?php
    
    namespace App\Controller\Admin;
    
    namespace App\Controller\Admin;
    
    use App\Entity\Product;
    use Doctrine\ORM\EntityManagerInterface;
    use Doctrine\ORM\QueryBuilder;
    use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
    use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
    use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
    use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
    use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
    use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\MoneyField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\ArrayField;
    use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
    use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\HttpFoundation\StreamedResponse;
    
    use App\Entity\User;
    
    class UserCrudController extends AbstractCrudController
    {
        public const ACTION_SAVE_CSV = "SAVE_CSV";
    
        // entity manager
        private $em;
    
        public function __construct(EntityManagerInterface $em){
            $this->em = $em;
        }
        
    
        public static function getEntityFqcn(): string
        {
            return User::class;
        }
    
        // configurable fields
        // see: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
        public function configureFields(string $pageName): iterable
        {
            return [
                TextField::new("username"),
                EmailField::new('email'),
                ArrayField::new('roles'),
            ];
        }
        public function configureActions(Actions $actions): Actions
        {
            $duplicate = Action::new(self::ACTION_SAVE_CSV)
                ->linkToCrudAction('saveUsersToCsv')
                ->setCssClass('btn btn-info')
                ->createAsGlobalAction();
    
            return $actions
                ->add(Crud::PAGE_INDEX,$duplicate);
        }
    
        public function saveUsersToCsv(
            AdminContext $context,
            AdminUrlGenerator $adminUrlGenerator,
            EntityManagerInterface $em
        ): Response {
    
            $userRepo = $this->em->getRepository(User::class);
            $users= $userRepo->findAll(); // Doctrine query
            $rows = array();
            $columns = array(
                'id',
                'email',
            );
            $rows[] = implode(',',$columns);
            foreach($users as $user){
                $data = array(
                    $user->getId(),
                    $user->getEmail(),
                );
                $rows[] = implode(',',$data);
            }
            $content = implode("\n",$rows);
            $response = new Response($content);
            $response->headers->set("Content-Type",'text/csv');
            $response->headers->set("Content-Disposition",'attachment; filename="users.csv"');
    
            return $response;
        }
    }
    

    The main problem was that I was not passing in the right arguments into the action function, and that I did not know at the time how to create a correct response.