[PHP] カッコいいCSVの取り込み方(ついでにfgetcsvでなくSplFileObjectを使ってみる)

業務系の開発だと CSV をよく扱う。その CSV を取り込む際に列番号とフィールドを紐付ける設計をよく目にする。何列目が何フィールドって CSV のそのままだからわかりやすいんだけど、項目が追加されたり順番を入れ替えたりすると面倒。仕様変更に弱い。で、項目名とフィールドを紐付けるような設計にしておけば項目追加も順番入れ替えも簡単。こういう発想を自分以外がしているのを見たことがない。よく使うので備忘録かつ周知のためにここに記す。ポイントは3つ。ちなみに fgetcsv でなく SplFileObject を使うのがモダンらしいw

・Shift-JIS だと文字化けするので tmp ファイルを利用する
・ヘッダーの項目とカラムの定義
・1行目で列数とカラム名を紐付け

// Shift-JIS だと文字化けするので tmp ファイルを利用する
setlocale(LC_ALL, 'ja_JP.UTF-8');
$file = file_get_contents("sample.csv");
$file = mb_convert_encoding($file, 'UTF-8', 'sjis-win');
$tmp = tmpfile();
fwrite($tmp, $file);
rewind($tmp);
$meta = stream_get_meta_data($tmp);

$csv = new SplFileObject($meta['uri']);
$csv->setFlags(SplFileObject::READ_CSV);

// ヘッダーの項目とカラムの定義
$headers = array(
'User.id' => 'ユーザーID',
'User.name' => 'ユーザー名',
'User.email' => 'メールアドレス',
);

$binding = array();
$records = array();
foreach ($csv as $row_no => $row) {
// 1行目で列数とカラム名を紐付け
if ($row_no == 0) {
for ($column_no=0;$column_no<count($row);$column_no++) {
$column_name = array_search($row[$column_no], $headers);
if ($column_name) {
$binding[$column_no] = $column_name;
}
}
}
else {
$record = array();
foreach ($binding as $column_no => $column_name) {
list($model, $field) = explode(".", $column_name);
if (isset($row[$column_no])) {
$record[$model][$field] = $row[$column_no];
}
}
$records[] = $record;
}
}
$this->ViewUserLog->saveAll($records);

ここでは CakePHP での実装を例に。ヘッダーの定義だけ修正すれば追加だろうが順番変更だろうが簡単。仕様変更怖くない。

かわのくんとは

Web系IT企業でプログラミングやマネジメントをしています。趣味で音楽を少々。

Youtubeでライブ動画配信中

Ustreamでライブ動画配信中

スマートフォン向けにPCサイトを自動変換(コンバート)する『CONV2SP』 CSS作成支援ツール『CSSツクール』