開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. PHP >
  4. Laravel >
  5. 【Laravel】CSVデータを書き出すCSV Mangaerを作ってみた。

【Laravel】CSVデータを書き出すCSV Mangaerを作ってみた。

こんにちは。
ニシザワです。

今回はLaravel専用にCSVファイルに書き出すCsv Managerを作ってみましたので紹介します。
まずは、Csv Manager全体のソースコードです。
<?php

declare(strict_types=1);

namespace App\Services\Data;
use App\Models\Data\File;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Response;
use Storage;
use Illuminate\Filesystem\FilesystemAdapter;

/**
 * Class CsvManager
 * @package App\Services\Data
 */
class CsvManager{

    /**
     * @var Collection
     */
    protected $data;

    /**
     * @var callable
     */
    protected $callbackFormat;

    /**
     * @var array
     */
    protected $csvHeader;

    /**
     * Dataの読み込み
     * @param Collection $data
     * @return CsvManager
     */
    public function loadData(Collection $data) : CsvManager
    {
        $this->data = $data;
        return $this;
    }

    /**
     * 書き出し用コールバック
     * @param callable $callbackFormat
     * @return CsvManager
     */
    public function callbackFormat(callable $callbackFormat) : CsvManager
    {
        $this->callbackFormat = $callbackFormat;
        return $this;
    }

    /**
     * CSVヘッダーの挿入
     * @param array $csvHeader
     * @return CsvManager
     */
    public function header(array $csvHeader) : CsvManager
    {
        $this->csvHeader = $csvHeader;
        return $this;
    }

    /**
     * CSVファイル作成
     * @param string $filename
     * @return string
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     * @throws \Exception
     */
    private function outputCsv(string $filename){
        $file = new \SplFileObject(storage_path("tmp"), 'w');

        // 多重配列の場合ネストをループ
        if (is_array(current($this->csvHeader))) {
            foreach ($this->csvHeader as $csvHeader) {
                mb_convert_variables('sjis-win', 'UTF-8', $csvHeader);
                $file->fputcsv($csvHeader);
            }
        }else{
            mb_convert_variables('sjis-win', 'UTF-8', $this->csvHeader);
            $file->fputcsv($this->csvHeader);
        }

        flush();

        $this->data->each(function(Model $model) use($file){
            if (! is_null($this->callbackFormat)) {
                $row = ($this->callbackFormat)($model);
            }

            // 多重配列の場合ネストをループ
            if (is_array(current($row))) {
                foreach ($row as $r) {
                    mb_convert_variables('sjis-win', 'UTF-8', $r);
                    $file->fputcsv($r);
                }
            }else{
                mb_convert_variables('sjis-win', 'UTF-8', $row);
                $file->fputcsv($row);
            }
            flush();
        });
        $output = \File::get($file->getRealPath());
        unlink($file->getRealPath());
        $this->put($filename, $output);
        return $output;
    }

    /**
     * CSVダウンロード
     * @param string $filename
     * @return Response
     * @throws \Exception
     */
    public function download(string $filename) : Response
    {
        $output = $this->outputCsv($filename);
        return new Response($output, 200, array(
            'Content-Type' => 'application/octet-stream',
            'Content-Disposition' =>  'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
        ));
    }
}

基本的にチェーンメソッドで必要な情報や処理を埋め込んでからdownload メソッドを呼び出せばダウンロードできるようにしています。

使い方

書き込むデータの取得用コールバックを作成

$callback = function(Model $model){
    $csvBody = [];
    $row = [];
    $row[] = $model->id;
    $row[] = $model->name;
    $row[] = $model->getTable();
    $csvBody[] = $row;
    foreach ($model->relations as $relation){
        $row = [];
        $row[] = $relation->id;
        $row[] = $relation->name;
        $row[] = $relation->getTable();
        $csvBody[] = $row;
    }
    return $csvBody;
};
モデルとリレーション先のモデルの情報を取得する方法です。
出力したい情報を配列で出力できるように無名関数を作ってください。

ダウンロード処理

  $csvManger = new CsvManager();
    $csvManger
    ->loadData($models) //EloquentBuilderで取得したCollection
    ->callbackFormat($callback)
    ->header(["id", "名前", "テーブル名"])
    ->download($filename)
loadDataはEloquentのCollectionを入れます。
callbackFormatは先程作成した出力用の無名関数を入れます。
headerはCSVの最初に入れるヘッダー情報です。
downloadはそのままResponseを返しているのでControllerの最後の返り値に入れていただければ大丈夫です。

これだけ作っておけば柔軟にモデルから取得してCSVファイルに吐き出すことができるので良かったら参考にしてみてください。
TOPに戻る