backend Mar 04

Easy CSV export in Api Platform

2 min read –

Like many other things in ApiPlatform, implementing export to CSV is very simple. Actually, all you need to do is to add a bit of configuration to your route, and additionally, if your entity is not the exact representation of the fields you would like to have in the CSV document, you can create a DTO and DataTransformer which will populate data to fit your needs.

So let’s get started with creating export for our Order entity:

<?php
namespace App\Entity;

use App\Entity\Money;
use App\Entity\Customer;
use ApiPlatform\Core\Annotation\ApiResource;

/** @ApiResource()*/
class Order
{
  /** @var int */
  private $id;
  
  /** @var string */
  private $orderNumber;
  
  /** @var \DateTime */
  private $orderDate;
  
  /** @var Customer */
  private $customer;
  
  /** @var Money */
  private $amount;
  
  /** @var string */
  private $status;
  
  // ...
}

The first step is to create a DTO with fields we would like to have in the CSV file. After that, we added the serialization group order-export which will be our normalization-context group for exporting endpoint.

<?php
namespace App\DTO;

use Symfony\Component\Serializer\Annotation\Groups;

final class OrderExport
{
    /**
     * @var string
     * @Groups({"order-export"})
     */
    private $orderStatus;

    /**
     * @var string
     * @Groups({"order-export"})
     */
    private $orderNumber;

    /**
     * @var \DateTime
     * @Groups({"order-export"})
     */
    private $orderDate;

    /**
     * @var string
     * @Groups({"order-export"})
     */
    private $customerFullName;

    /**
     * @var string
     * @Groups({"order-export"})
     */
    private $customerEmail;

    /**
     * @var string
     * @Groups({"order-export"})
     */
    private $customerPhoneNumber;

    /**
     * @var string
     * @Groups({"order-export"})
     */
    private $orderTotal;
  
   // ...
}

Next step is to create a DataTransformer which will transform Order entity to DTO object:

<?php
namespace App\DataTransformer;

use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use App\DTO\OrderExport;
use App\Entity\Order;
use App\Entity\Customer;

class OrderExportDataTransformer implements DataTransformerInterface
{
    public function transform($data, string $to, array $context = [])
    {
        $orderExport = new OrderExport();
      
        $orderExport->setOrderNumber($data->getOrderNumber());
        $orderExport->setOrderStatus($data->getStatus());
        $orderExport->setOrderDate($data->getOrderDate());
        $orderExport->setOrderTotal($data->getAmount()->getFormatted());

        $customer = $data->getCustomer();
      
        $orderExport->setCustomerFullName($customer->getFirstName() .' '.$customer->getLastName());
        $orderExport->setCustomerEmail($customer->getEmail());
        $orderExport->setCustomerPhoneNumber($customer->getPhone());
     
        return $orderExport;
    }

    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        return OrderExport::class === $to && $data instanceof Order;
    }
}

Finally, we are ready to create the export route in Order entity:

<?php
namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;

/**
 * @ApiResource(
 *  collectionOperations={
 *     "export"={
 *          "method"="GET",
 *          "path"="/orders/export",
 *          "formats"={"csv"={"text/csv"}},
 *          "pagination_enabled"=false,
 *          "output"=OrderExport::class,
 *          "normalization_context"={"groups"={"order-export"}}
 *     }
 *   }
 * )
 */
class Order
{ 
  // ...
}

There are a few important things in this configuration:

  • “formats”={“csv”={“text/csv”}} -> Here we set the route format to CSV
  • “pagination_enabled”=false -> In export we want to show all orders, not just the ones on the first page. Therefore we disabled pagination for this route.
  • “output”=OrderExport::class -> This line is telling ApiPlatform to use OrderExport DTO we prepared, instead of the Order entity.
  • “normalization_context”={“groups”={“order-export”}} -> Here we set the serialization group we used in DTO

And that’s it! Now we can send a request to /orders/export and we’ll get the CSV. Since we didn’t implement custom export, we can still leverage from built-in API Platform features. Therefore we can use all available filters just like we do on regular endpoints for Order entity.

In fact, this is not just a way to make the CSV export – you can use it for any other format too.