基盤系の開発などをしていると、売上データや顧客データなどが書かれたExcelファイルや、CSVファイルを読み込んで、DBに格納するというシーンがボチボチあるため、対応しやすいようQiitaにまとめておく。
#環境#
PHP 7.1.7
Laravel 5.6.33(pureでも問題ない、使うのはpure標準の関数だか開発した環境がたまたまLaravelだっただけ)
MySQL 5.7.22
#使う主な関数#
###fopen()###
第一引数に指定したファイルを第二引数で指定したファイルモードで開く。
今回の場合はCSVファイルを開くのに使用する。
ファイルモードに関してはドキュメントを参照するのが一番良いと思う。とりあえず今回使うのは、読み込み専用でファイルがなかった場合falseを返す、'r'となる。
fopen('指定したファイル', 'ファイルモード');
###fgetcsv()###
引数に指定したfopen()関数を通過した後のCSVファイルを行ごとに配列化する。
fopen()関数を通したあとでないと使えないので注意
fgetcsv(引数);
###fclose()###
引数に指定したファイルを閉じる。
今回はCSVファイルを閉じるのに使用する。
fclose(引数);
#手順#
流れとしては、
①fopen()関数でCSVファイルを開く。
②CSVファイルをfgetcsv()関数で行ごとに抽出(行ごと抽出なので回数回してあげないと抽出できない)
③fclose()関数でCSVファイルを閉じる。
となる。
View側Formの内容に関しては詳しい話は省略するが、
{!! Form::open(['route' => 'hoge.hoge', 'files' => true]) !!}
{{Form::file('csvfile')}}
{{Form::submit('送信')}}
{!! Form::close() !!}
こんな感じでしょう。type属性は'csvfile'としている。
またForm::open()メソッドの'files'キーを'true'にしておかないとファイルアップロードの対応をしてくれないので忘れずに
まずはCSVファイルを開いてあげる。
public function hoge(Request $request)
{
$input = $request->all();
$csvfile = $input['csvfile'];
$importfile = fopen($csvfile, "r");
}
fopen()関数の第一引数にはformから送られてきたCSVファイルが代入されている'$csvfile'が,第二引数には読み込み専用のファイルモードが記述されている。
ちなみに今回はForm経由のCSVファイルを使っているわけだが、fopen()関数の第一引数に直接Pathを記述して指定することもできるので、もし挙動チェックなどをしたい場合などに使うと良いだろう。
続いて、fgetcsv()関数を用いてCSVファイルの行ごと取得を行っていく。
while($line = fgetcsv($importfile)) {
//headerのスキップ処理
if($line[0] == 'id') {
continue;
}
$importline = [
'hoge' => $line[1],
'fuga' => $line[2]
];
$array[] = $importline;
}
今回はwhile文で処理してみた。
注意点はCSVファイルだと大概「ヘッダー」(id,名前,年齢,性別的なやつ)が記述されているとおもうが、それも読み込んでしまうので条件式でスキップさせる必要がある。(上記ではfgetcsvで$lineに行の内容を配列として取得した際に、$line[0]がidという文字列だった場合、処理を飛ばしてあるが、ここは自分がクールだと思う方法で飛ばしてあげて良いと思う。。)
あとは$lineの内容を連想配列化し、その連想配列をさらに配列に入れ多次元配列化してあげる。この処理は$lineがfalseを返すまで繰り返される。
つづいてDBに格納する。
while() {
...
}
DB::insert($array);
fclose($importfile);
}
DBへ格納する方法はいくつかあるとは思うが、今回注意なのはsave()メソッドを使うと最後尾の行しか反映されないので、insertメソッドで処理すると全行をDBへ保存できる。(DBの部分はDBファサードで置き換え、DBファサードはuseしておく必要があるので忘れずに)
最後にfclose()関数でCSVファイルを閉じてあげる。
あとはトップページなどにリダイレクトしてあげれば処理としては終了
割と急ぎだったけどまとめとしてはこんな感じでしょうか
#まとめコード#
全部まとめるとこんな感じ
public function hoge(Request $request)
{
$input = $request->all();
$csvfile = $input['csvfile'];
$importfile = fopen($csvfile, "r");
while($line = fgetcsv($importfile)) {
//headerのスキップ処理
if($line[0] == 'id') {
continue;
}
$importline = [
'hoge' => $line[1],
'fuga' => $line[2]
];
$array[] = $importline;
}
DB::insert($array);
fclose($importfile);
return redirect()->to('遷移したいページ');
}