Export Laravel Resource to CSV trait

This post was originally shared on Medium.com

I was looking for a simple method of exporting a Laravel Resource to a CSV file, there is no native method of doing this however as you’ve already modelled how your data should look in an API it seems a little backward to re-model your data for exporting, it’s much nicer to re-use the Resource that you’ve already pre-defined.

I found the below to be reasonably elegant way of exporting a Laravel resource to CSV file as a trait on the Resource. This handles nested data by appending the parent key with an underscore affix and iterates hierarchically where needed. This solution makes use of an AnonymousResourceCollection (the same approach laravel takes when you run Resource::collection($data); ).

Note: This relies on League\CSV to generate the CSV file.

ExportTrait.php:

<?php

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use League\Csv\Writer;

trait ExportTrait
{
    /**
     * Exports a CSV from a provided resource collection
     * @param mixed $resource
     * @param string $filename
     * @return int
     * @throws \League\Csv\CannotInsertRecord
     */
    public static function exportCSV($resource, $filename = 'export.csv'): int
    {
        $resourceCollection = new AnonymousResourceCollection($resource, static::class);
        $data = $resourceCollection->toArray(request());
        $csv = Writer::createFromFileObject(new \SplTempFileObject());

        $i = 0;
        // Build the CSV
        foreach ($data as $key => $datum) {
            // Add the headers into the document
            $row = self::assocToFlat($datum);
            if ($i === 0) {
                $csv->insertOne(array_keys($row));
            }

            $csv->insertOne($row);
            $i++;
        }

        return $csv->output($filename);
    }

    /**
     * Turns an associative array into a flat array, with hierarchical key names joined by underscore
     * @param $dataRow
     * @param string $prefixKey
     * @return array
     */
    private static function assocToFlat($dataRow, $prefixKey = ''): array
    {
        $return = [];
        foreach ($dataRow as $key => $item) {
            if (!is_array($item)) {
                $return[$prefixKey . $key] = $item;
                continue;
            }

            $return = array_merge($return, self::assocToFlat($item, $prefixKey . $key . '_'));
        }

        return $return;
    }
}

Add the trait to a resource you want to export:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;
use Support\Resource\ExportTrait;

class MyResource extends JsonResource
{
    use ExportTrait;

    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        parent::toArray($request);
    }
}

Then, when you have your data available, the following command will set the necessary download headers and export a CSV file based on your resource specification.

MyResource::export($data);