PHP
laravel

LaravelでCSVダウンロード。

More than 3 years have passed since last update.

CSVダウンロード

LaravelでDBにある情報をCSVとしてダウンロードさせる。
そのときの検索結果に紐づくリストなのでCSVファイルとして保存しない。

app/routes.php
<?php
// CSVダウンロード
Route::get('/csv', function() {
    $users = \User::where('type', '=', '1')->get(['name', 'birth_day'])->toArray();
    $stream = fopen('php://output', 'w');
    foreach ($users as $user) {
      fputcsv($stream, $user);
    }
    $headers = array(
        'Content-Type' => 'text/csv',
        'Content-Disposition' => 'attachment; filename="users.csv"',
    );
    return Response::make('', 200, $headers);
});

素直にfputcsvを使うのが良いみたいだ。

【修正】文字コード変換

UTF-8のままだとエクセルで文字化けするのでSJIS-winに文字コードを変換。
php://outputだとrewind()、fseek()が使えずファイルポインタを先頭に戻せないのでphp://tempを使用。

app/routes.php
<?php
// CSVダウンロード
Route::get('/csv', function() {
    $users = \User::where('type', '=', '1')->get(['name', 'birth_day'])->toArray();
    $stream = fopen('php://temp', 'r+b');
    foreach ($users as $user) {
      fputcsv($stream, $user);
    }
    rewind($stream);
    $csv = str_replace(PHP_EOL, "\r\n", stream_get_contents($stream));
    $csv = mb_convert_encoding($csv, 'SJIS-win', 'UTF-8');
    $headers = array(
        'Content-Type' => 'text/csv',
        'Content-Disposition' => 'attachment; filename="users.csv"',
    );
    return Response::make($csv, 200, $headers);
});

【修正】ヘッダー行追加。

app/routes.php
<?php
// CSVダウンロード
Route::get('/csv', function() {
    $users = \User::where('type', '=', '1')->get(['name', 'birth_day'])->toArray();
    $csvHeader = ['名前', '誕生日'];
    array_unshift($users, $csvHeader);   
    $stream = fopen('php://temp', 'r+b');
    foreach ($users as $user) {
      fputcsv($stream, $user);
    }
    rewind($stream);
    $csv = str_replace(PHP_EOL, "\r\n", stream_get_contents($stream));
    $csv = mb_convert_encoding($csv, 'SJIS-win', 'UTF-8');
    $headers = array(
        'Content-Type' => 'text/csv',
        'Content-Disposition' => 'attachment; filename="users.csv"',
    );
    return Response::make($csv, 200, $headers);
});

【修正】ファサード対応

CSVクラス

app/Lib/CSV.php
<?php namespace MyProject\Lib\CSV;

use Response;

class CSV
{    
    public function __construct()
    {
    }

  /**
     * CSVダウンロード
     * @param array $list
     * @param array $header
     * @param string $filename
     * @return \Illuminate\Http\Response
     */
    public function download($list, $header, $filename)
    {
        if (count($header) > 0) {
            array_unshift($list, $header);
        }
        $stream = fopen('php://temp', 'r+b');
        foreach ($list as $row) {
            fputcsv($stream, $row);
        }
        rewind($stream);
        $csv = str_replace(PHP_EOL, "\r\n", stream_get_contents($stream));
        $csv = mb_convert_encoding($csv, 'SJIS-win', 'UTF-8');
        $headers = array(
            'Content-Type' => 'text/csv',
            'Content-Disposition' => "attachment; filename=$filename",
        );
        return \Response::make($csv, 200, $headers);
    }
}

サービスプロバイダー

app/Lib/CSVServiceProvider.php
<?php namespace MyProject\Lib\CSV;

use Illuminate\Support\ServiceProvider;

class CSVServiceProvider extends ServiceProvider
{

    public function register()
    {
        $this->app->bindshared('csv', function()
        {
            return new CSV;
        });

    }

}

ファサード

app/Facades/CSV.php
<?php namespace MyProject\Facades;

use Illuminate\Support\Facades\Facade;

class CSV extends Facade
{

    public static function getFacadeAccessor()
    {
        return 'csv';
    }

}

オートロード更新。

composer dump-autoload

パス追記

app/config/app.php
    'providers' => array(
       ……………………
    'MyProject\CSV\CSVServiceProvider',// 追記

    'aliases' => array(
       ……………………
    'CSV'        => 'MyProject\Facades\CSV',// 追記

コントローラー修正

app/routes.php
<?php
// CSVダウンロード
Route::get('/csv', function() {
    $users = \User::where('type', '=', '1')->get(['name', 'birth_day'])->toArray();
    $csvHeader = ['名前', '誕生日'];
    return CSV::download($users, $csvHeader, 'user_list.csv');
});