目的
・CSVファイルをエクスポート、インポートする。
・エクスポートしたCSVファイルを、Excelなどで編集した後、そのままインポートしてデータを更新できるようにする。
サンプルデータ
Entry というMVCモデルを使うことにします。
・entries テーブルの構造
エクスポート
CSVヘルパーの作成
app/views/helpers/ フォルダ内に、「csv.php」という名前のファイルを作成。
以下コードをコピペします。
<?php
class CsvHelper extends AppHelper {
var $delimiter = ',';
var $enclosure = '"';
var $filename = 'Export.csv';
var $line = array();
var $buffer;
function CsvHelper() {
$this->clear();
}
function clear() {
$this->line = array();
$this->buffer = fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+');
}
function addField($value) {
$this->line[] = $value;
}
function endRow() {
$this->addRow($this->line);
$this->line = array();
}
function addRow($row) {
fputcsv($this->buffer, $row, $this->delimiter, $this->enclosure);
}
function renderHeaders() {
header("Content-type:application/vnd.ms-excel");
header("Content-disposition:attachment;filename=".$this->filename);
}
function setFilename($filename) {
$this->filename = $filename;
if (strtolower(substr($this->filename, -4)) != '.csv') {
$this->filename .= '.csv';
}
}
function render($outputHeaders = true, $to_encoding = null, $from_encoding = "auto") {
if ($outputHeaders) {
if (is_string($outputHeaders)) {
$this->setFilename($outputHeaders);
}
$this->renderHeaders();
}
rewind($this->buffer);
$output = stream_get_contents($this->buffer);
if ($to_encoding) {
$output = mb_convert_encoding($output, $to_encoding, $from_encoding);
}
return $this->output($output);
}
}
コントローラーにメソッド追加(entries_controller.php)
・作成したCSVヘルパーの読み込み
class EntriesController extends AppController {
var $name = 'Entries';
var $helpers = array('Csv');
/*(省略)*/
・エクスポート用のメソッドを作成(csv_export)
function csv_export() {
Configure::write('debug', 0);
$this->layout = false;
$filename = date('YmdHis');
$th = array('id', 'name', 'email');
$th_name = array('id', '名前', 'メールアドレス');
$td = $this->Entry->find('all', array('fields' => $th));
$this->set(compact('filename', 'th_name', 'td'));
}
ビューの作成(csv_export.ctp)
<?php
$csv->addRow($th_name);
foreach ($td as $t) {
$csv->addRow();
$csv->addField($t['Entry']['id']);
$csv->addField($t['Entry']['name']);
$csv->addField($t['Entry']['email']);
$csv->endRow();
}
$csv->setFilename($filename);
echo $csv->render(true, 'sjis', 'utf-8');
?>
実行
「/entries/csv_export」へアクセスすれば、エクスポートが実行されます。
エクスポートしたCSVファイルを、エクセルで開くとこんな感じ↓
インポート
ビューの作成(csv_import.ctp)
<?php echo $form->create('Entry', array('type' => 'file')); ?>
<?php echo $form->file('file_name'); ?>
<?php echo $form->end('実行'); ?>
コントローラーにメソッド追加(entries_controller.php)
・インポート用のメソッドを作成(csv_import)
function csv_import(){
if (!empty($this->data)) {
$up_file = $this->data['Entry']['file_name']['tmp_name'];
$fileName = TMP.'/csv/'.$this->data['Entry']['file_name']['name'];
if (is_uploaded_file($up_file)){
move_uploaded_file($up_file, $fileName);
$this->Entry->begin();
try {
$csvData = file($fileName, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
foreach($csvData as $key => $line){
if ($key != 0) { // 1行目は飛ばす
$record = split(",", $line);
mb_language("Japanese");
$data = array(
'id' => mb_convert_encoding($record[0], "UTF-8", "auto"),
'name' => mb_convert_encoding($record[1], "UTF-8", "auto"),
'email' => mb_convert_encoding($record[2], "UTF-8", "auto"),
);
$this->Entry->create($data);
$this->Entry->save();
}
}
$this->Entry->commit();
} catch(Exception $e) {
$this->Entry->rollback();
}
}
}
}
※4行目: アップロード先のフォルダは任意で構いませんが、ここでは app/tmp フォルダ内に csv フォルダを作成して、そこを指定しています。パーミッションの設定もお忘れなく。
※11行目: さきほどエクスポートしたCSVファイルを、そのままインポートする為、1行目(ラベル)を飛ばしています。
新規追加と更新
「id」の列に数値が入っていれば”上書き”、空なら”新規追加”となります。
・新規追加の場合
「id」の列を空にしておく。
データベースを空にしてからインポートする場合
インポートする前にテーブルのデータを空にするコードを追加します。
ソースコードで言うと、8行目の次。
//(省略)
try {
$this->Entry->deleteAll('1 = 1', false);
$csvData = file($fileName, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
//(省略)
バリデーション
CSVファイルをインポートする際も、モデルに記述されたバリデーションルールは適用されます。
例えば、nameフィールドに”入力必須”のバリデーションルールを設定していた場合、「name」の列が空だとインポートされません。
一部のデータにミスがあると、他すべてのデータがインポートされません。
インポートが成功したのか失敗したのかを明示したいならば、setFlash等を使うといいと思います。
//(省略)
if ($this->Entry->save()) {
$this->Session->setFlash('インポートに成功しました');
} else {
$this->Session->setFlash('インポートに失敗しました');
}
//(省略)
おまけ
データベースに格納する値が、”文字列”ではなく”数値”であることも多いと思います。
例えば、「sex(性別)」フィールドがある場合、文字列ではなく数値で格納しているかもしれません。
(1:男、2:女)
数値を文字列に変換してエクスポートする方法
・コントローラ
function csv_export() {
Configure::write('debug', 0);
$this->layout = false;
$filename = date('YmdHis');
$th = array('id', 'name', 'sex');
$th_name = array('id', '名前', '性別');
$td = $this->Entry->find('all', array('fields' => $th));
$sexArr = array('1' => '男', '2' => '女');
$this->set(compact('filename', 'th_name', 'td', 'sexArr'));
}
・ビュー
<?php
$csv->addRow($th_name);
foreach ($td as $t) {
$csv->addRow();
$csv->addField($t['Entry']['id']);
$csv->addField($t['Entry']['name']);
$csv->addField($sexArr[$t['Entry']['sex']]);
$csv->endRow();
}
$csv->setFilename($filename);
echo $csv->render(true, 'sjis', 'utf-8');
?>
・エクスポートされるCSVデータ
ただし、この方法でエクスポートしたCSVファイルでは、「エクスポート→Excelで編集→そのままインポート」は不可になります。
エクスポートだけする場合に有効です。





Comments
こんにちは。CSVファイルのエクスポート、インポート機能を探してこちらにたどり着きました。
cakePHP2.xで実装したいのですが、こちらは2.xは非対応でしょうか?
こちらは1.3系の記事です。
1.3系と2.x系では根本的な設計が違いますので、このコードは使えないと思います。すみません。。
了解しました。ありがとうございました。