9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel で API リソースを使って CSV ダウンロードする

Last updated at Posted at 2019-07-11

はじめに

Laravel で CSV ダウンロードするときのアーキテクチャはいくつかある。

けど、コントローラからモデルをとって、整形して、CSV にするなら、
標準の JSON API リソースが JSON じゃなくて CSV になるだけでもよいのではないかと思った。
そして意外となかったので、書いてみます。

CSV API リソースを実装する

標準の JSON API リソースの実装を見て、最低限な次の機能を実装しました。

  • CSV だと複数のレコードを扱うように思うので、コレクションを扱う ResourceCollection のみ
  • 整形とダウンロードのレスポンス生成のみ(ほかにも機能があるっぽい)
ResourceCollection.php
<?php

namespace App\Http\Resources\Csv;

use Illuminate\Contracts\Support\Responsable;

abstract class ResourceCollection implements Responsable
{
    protected $collection;

    public function __construct($resource)
    {
        $this->collection = $resource;
    }

    public function toResponse($request)
    {
        return response()->streamDownload(function () {
            $file = new \SplFileObject('php://output', 'wb');

            $header = $this->attributes();
            if (! empty($header)) {
                $file->fputcsv($this->convertCharset($header));
            }

            foreach ($this->toArray() as $fields) {
                $file->fputcsv($this->convertCharset($fields));
            }
        }, $this->fileName());
    }

    private function convertCharset($fields)
    {
        return collect($fields)
            ->map(function ($item) {
                return mb_convert_encoding($item, 'SJIS-win', mb_internal_encoding());
            })
            ->all();
    }

    /**
     *  見出しがあれば返します
     */
    protected function attributes()
    {
        return [];
    }

    /**
     *  整形した配列を返します
     */
    abstract protected function toArray();

    /**
     *  ダウンロードファイルの名前があれば返します
     */
    protected function fileName()
    {
        return 'download.csv';
    }
}

使ってみる

注文情報を CSV に書き出すことを考えてみましょう。

コントローラ

ほとんど JSON のときと同じです。

OrderController.php
<?php

namespace App\Http\Controllers;

use App\Order;
use App\Http\Resources\ReportResource;

class OrderController extends Controller
{
    public function report()
    {
        return new ReportResource(
            Order::query()
                ->where('is_canceled', false)
                ->get()
        );
    }
}

API リソース

JSON のときと同じく、toArray をオーバライドして整形します。
加えて、attributes で見出しを、fileName でダウンロードファイル名を、オーバライドして指定することもできます。

ReportResource.php
<?php

namespace App\Http\Resources;

use App\Http\Resources\Csv\ResourceCollection;

class ReportResource extends ResourceCollection
{
    protected function toArray()
    {
        $this->collection->load(['user', 'products']);

        return $this->collection
            ->map(function ($order) {
                return [
                    $order->id,
                    $order->user->id,
                    $order->user->name,
                    $order->products->sum('price'),
                ];
            })
            ->all();
    }

    protected function attributes()
    {
        return [
            '注文ID',
            '会員ID',
            '会員名',
            '注文金額合計',
        ];
    }

    protected function fileName()
    {
        return '注文一覧.csv';
    }
}

ダウンロード例

注文一覧.csv
注文ID,会員ID,会員名,注文金額合計
1,1,ほげほげ,1000
2,1,ほげほげ,1500
3,2,ふがふが,1000
4,2,ふがふが,2000

おわりに

JSON のときと同じ使い方ができるので、覚えることが少ないし、標準にも近いかもしれない。

すっきり!

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?