LaravelでCSVを扱うことがありましたので備忘録として残しておきます。
SplFileObjectクラスでCSVファイルを読み込む
PHP,LaravelでCSVファイルを読み込む方法を調べているとfile_get_contents()メソッドは使わないほうがいいと書いてあることが多いので、今回はSplFileObjectクラスを使用します。
use Carbon\Carbon;
public function run()
{
$file = new SplFileObject('database/csv/facility.csv'); // ファイルの読み込み
$file->setFlags(
\SplFileObject::READ_CSV | // CSV 列として行を読み込む
\SplFileObject::READ_AHEAD | // 先読み/巻き戻しで読み出す。
\SplFileObject::SKIP_EMPTY | // 空行は読み飛ばす
\SplFileObject::DROP_NEW_LINE // 行末の改行を読み飛ばす
);
$list = [];
$now = Carbon::now();
// 一行ずつ処理
foreach ($file as $index => $line) {
$list[] = [
"user_id" => $line[0], // 行の1列目
"facility_name" => $line[1], // 行の2列目
'pref' => $line[2],
'city' => $line[3],
'address' => $line[4],
'building' => $line[5],
"created_at" => $now,
"updated_at" => $now,
];
}
}
DB::table("facilities")->insert($list);
}
エラー
CSVファイルを作成してSeederを走らせると下記のようなエラーが出ました。
General error: 1366 Incorrect integer value: '1'
あらら…数字が文字列だったのかな?と思いintval()を使いキャストしてから再度シーディングすると今度は下記のようなエラーが表示されました。
Integrity constraint violation: 1217 Cannot delete or update a parent row: a foreign key constraint fail
外部キーとの整合性が取れていない??どうして??と思い調べてみるとどうやらCSVファイルの1行目の先頭が0になっていることがわかりました。
CSVファイルの1行目の先頭が0になる
ググってみるとCSVファイルを意識せずにダウンロードすると先頭に意識していない3バイト、BOMが入っていることがわかりました。
https://qiita.com/PET_HAL/items/fd1364513f81562671b6
ということで対策します。
解決策
エディタなどでCSVファイルをBOMなしに変更するのが簡単かと思いますが、コードで対応したかったので下記のようになりました。
use Carbon\Carbon;
public function run()
{
$file = new SplFileObject('database/csv/facility.csv');
$file->setFlags(
\SplFileObject::READ_CSV | // CSV 列として行を読み込む
\SplFileObject::READ_AHEAD | // 先読み/巻き戻しで読み出す。
\SplFileObject::SKIP_EMPTY | // 空行は読み飛ばす
\SplFileObject::DROP_NEW_LINE // 行末の改行を読み飛ばす
);
$list = [];
$now = Carbon::now();
foreach ($file as $index => $line) {
// 先頭に意識していない3バイトのBOMが含まれている時に対応
if ($index === 0) {
$line[0] = preg_replace('/^\xEF\xBB\xBF/', '', $line[0]);
}
$intId = intval($line[0]); //念のためキャストする
if (!is_null($intId) && $intId !== 0){
$list[] = [
"user_id" => $intId,
"facility_name" => $line[1],
'pref' => $line[2],
'city' => $line[3],
'address' => $line[4],
'building' => $line[5],
"created_at" => $now,
"updated_at" => $now,
];
}
}
DB::table("facilities")->insert($list);
}