2019/08/26
連載『CSVダウンロードの作成』シリーズの最終回です。
今回はデータベースのレコードをCSVに出力してダウンロードするコードをエントリーします。
この回では、Laravelの知識というよりも、PHPでのSCV出力の知識が必要になります。
イントロの回でも事前に必要になる知識を紹介しているのでご確認ください。
演習の続きをはじめる前にまずは PHP での CSV 生成方法について解説します。
PHPでのCSV生成方法について
PHPでのCSV生成方法は主に4つやり方があります。
2.いったんサーバ内にファイル保存する方式
3.メモリ内に一時保存する方式
4.ブラウザに直接返却する方式
1.変数に出力テキストを追加していく方式
入門書やブログなどで紹介されている方法です。
しかし、$datasが100件なら問題ありませんが、数千件になるようであればCPU使用率を振り切るかメモリを使いきって落ちたりします。
:
$buf = '';
$datas = array(
array(…),
:
);
foreach($datas as $data){
$buf .= '"'.implode(‘”,”‘, $data).'"'.PHP_EOL;
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=hoge.csv');
print $buf;
2.いったんサーバ内にファイル保存する方式
サーバローカルにfopenしたファイルに対して、1行づつCSVを追記していきます。
追記が全部終わったところでreadfile
して返却するという方式です。
:
$tmp_file = 'tmp.csv';
$fp = fopen($tmp_file,'a');
$datas = array(
array(…),
:
);
foreach($datas as $data){
fputcsv($fp, $data, ',','"');
}
fclose($fp);
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=hoge.csv');
readfile($tmp_file);
unlink($tmp_file);
3.メモリ内に一時保存する方式
php://memory
やphp://temp
などのストリームラッパーを使用して、データをメモリに保存していくという方法です。
今回の演習ではこの方法でCSVをダウンロードします。
php://memoy と php://temp の違い
php://memory は全てをメモリ上に保持します。
対して php://temp は一定以上の容量に達した場合、自動的にテンポラリファイルに逃がされます。
php://memory 一択のように思えますが、結果データの容量が読めない場合は php://temp の方が安全です。
:
$fp = fopen('php://temp/maxmemory:5242880','a');
$datas = array(
array(…),
:
);
foreach($datas as $data){
fputcsv($fp, $data, ',','"');
}
header(“Content-Type: application/octet-stream”);
header(“Content-Disposition: attachment; filename=hoge.csv”);
rewind($fp);
print stream_get_contents($fp);
fclose($fp);
4.ブラウザに直接返却する方式
ファイル保存の必要が別途必要でない場合は、php://output で直接ブラウザに返却する方法が最短の手段になります。
:
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=hoge.csv");
$fp = fopen('php://output','w');
$datas = array(
array(…),
:
);
foreach($datas as $data){
fputcsv($fp, $data, ',','"');
}
fclose($fp);
CSVダウンロード機能の作成手順
ビュー
CSVダウンロードボタンです。URLヘルパーを使ってリンク先を記述します。
:
<p><a class="btn btn-primary" href="{{url('/csv/download1')}}" target="_blank"> CSV download その1</a></p>
:
ルーティング
上記のリンク先に対応するコントローラを記述します。
Route::get('csv/download1', 'CsvDownloadController@download1'); //ダウンロード
CSV出力するカラムの定義(コントローラ)
private function csvcolmns()
{
$csvlist = array(
'email' => 'email',
'password' => 'password',
'name' => '名前',
'address' => '住所',
'birthdate' => '生年月日',
'msg' => 'メッセージ',
);
return $csvlist;
}
解説
CSVに出力するカラムを設定しておきます。このメソッドは後で呼び出します。
取り出すときは foreach
を使います。
key
はデータベースのカラム名です。
value
はカラム名を説明する文言です。CSV出力ファイルではヘッダとして出力する部分です。
CSV出力(コントローラ)
public function download1()
{
// 出力項目定義
$csvlist = $this->csvcolmns(); //auth_information + profiles
// ファイル名
$filename = "auth_info_profiles_".date('Ymd').".csv";
// 仮ファイルOpen
$stream = fopen('php://temp', 'r+b');
// *** ヘッダ行 ***
$output = array();
foreach($csvlist as $key => $value){
$output[] = $value;
}
// CSVファイルを出力
fputcsv($stream, $output);
// *** データ行 ***
$blocksize = 100; // QUERYする単位
for($i=0 ; true ; $i++) {
$query = \App\Models\AuthInformation::query();
$query->Join('profiles','auth_information.id','=','profiles.authinformation_id'); //内部結合
$query->orderBy('auth_information.id');
$query->skip($i * $blocksize); // 取得開始位置
$query->take($blocksize); // 取得件数を指定
$lists = $query->get();
//デバッグ
//$list_sql = $query->toSql();
//\Log::debug('$list_sql="' . $list_sql . '"');
// レコードあるか?
if ($lists->count() == 0) {
break;
}
foreach ($lists as $list) {
$output = array();
foreach ($csvlist as $key => $value) {
$output[] = str_replace(array("\r\n", "\r", "\n"), '', $list->$key);
}
// CSVファイルを出力
fputcsv($stream, $output);
}
}
// ポインタの先頭へ
rewind($stream);
// 改行変換
$csv = str_replace(PHP_EOL, "\r\n", stream_get_contents($stream));
// 文字コード変換
$csv = mb_convert_encoding($csv, 'SJIS-win', 'UTF-8');
// header
$headers = array(
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="'.$filename.'"',
);
return \Response::make($csv, 200, $headers);
}
解説
コントローラで定義したメソッドです。このメソッドでデータベースのレコードを抽出してCSVをダウンロードします。
最初にカラム名を出力して、次にその値を出力します。
Microsoft Excel が想定している日本語CSVのデフォルトエンコードは『Shift_JIS』です。
なので、最後にmb_convert_encodeing()
でエンコードしてあげます。
演習「CSVダウンロードの作成」に関しては以上です。
なお、PHP のファイルシステム関数についてもっと理解を深めたい方は 独習PHP 第3版 をお勧めします。
ファイルシステム関数の掲載ページの箇所
5.5.1 テキストファイルへの書き込み
5.5.2 ファイルを開く - fopen/fclose関数
5.5.3 fopen 関数でのエラー処理 - エラー制御演算子
5.5.4 ファイルへの書き込み - fwrite関数
5.5.5 ファイルのロック - flock関数
5.5.6 タブ区切りテキストの読み込み - fgetcsv関数
5.5.7 タブ区切りテキストの読み込み(別解) - fgets/file関数
5.5.8 ファイルシステム関数の設定パラメータ
仕事で Laravel を使っています。気づいたことや新しい発見など情報を発信していきます。問い合わせはこちら。