目的
・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系では根本的な設計が違いますので、このコードは使えないと思います。すみません。。
了解しました。ありがとうございました。