CakePHP 1.3.xで CSVファイルのエクスポート機能を作ってみた
2015/01/11
過去に CakePHP1.3 で作成したシステムにCSVファイルのエクスポート機能を追加してほしいときたので、機能追加してみました。
忘備録も兼ねたポストで次回に備えたいと思います。
『CSV Helper』でCSVを作成する
今回は CakePHPのバージョンが 1.3.22だったので、ヘルパーである『CSV Helper』を使ってみたいと思います。
ダウンロードというか、公開されているページはこちら。
CSV Helper (PHP 5) :: The Bakery: Everything CakePHP
CsvHelper
公開されているものを転載します。
最後の「?>」は要らないので、書きません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?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); } } |
アクションを作成
ダウンロード機能を実装するコントローラーに、ダウンロード用アクションを作成します。
そのまんま、download としてみました。
ヘルパー設定に「Csv」を記述するのを忘れずに。
サンプルのアクションは、都道府県と、欲しいデータ(ユーザ)の一覧を取得しています。
抽出したデータと、表の一行目の配列をセットしてビューに渡します。
app/controllers/users_controller.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
class UsersController extends AppController { // Model var $uses = array('User'); // Helper var $helpers = array('Html', 'Form', 'Csv'); public function download() { // 都道府県取得 $_prefs_master = $this->Prefecture->find( 'all', array( 'conditions' => null, 'fields' => null, 'order' => array('Prefecture.id' => 'ASC'), 'recursive' => -1 ) ); $prefs_master = Set::combine($_prefs_master, '{n}.Prefecture.id', '{n}.Prefecture.name'); // ユーザデータ抽出 $users = $this->User->find( 'all', array( 'conditions' => $conditions, 'fields' => null, 'order' => $order, 'recursive' => -1, ) ); // 警告を出さない Configure::write('debug', 0); // レイアウトなし $this->layout = ''; // 表の一行目を作成 $head = array('ID', '氏名', '登録日時', '電話番号', '都道府県', '市区町村', '以下住所'); // 任意のファイル名 $filename = 'UserList_' . date('Ymd_His'); // 出力 $this->set(compact('users', 'filename', 'head', 'prefs_master')); } } |
ビューファイル
最後にビューファイルの作成です。
app/views/download.ctp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/** * CSV書き出し用ビューファイル * */ $csv->addRow($head); // 一行目データ // controllerから渡されたデータを追加 foreach ( $users as $val ) { $csv->addRow(); $csv->addField($val['User']['id']); // ID $csv->addField($val['User']['name']); // 氏名 $csv->addField($val['User']['first_create']); // 登録日時 $csv->addField($val['User']['telephone']); // 電話番号 $csv->addField($prefs_master[$val['User']['prefecture']]); // 都道府県 $csv->addField($val['User']['city']); // 市区町村 $csv->addField($val['User']['address']); // 以下住所 $csv->endRow(); } // ファイル名設定 $csv->setFilename( $filename ); // 文字コードをUTF-8からSJISに変換して出力。 echo $csv->render(true, 'sjis', 'utf-8'); |
コントローラーから渡されたデータを foreach() でグリグリと回しているだけです。
基本的に、CakePHPで作成したアプリケーションの文字コードはUTF-8だと思いますので、csvにする時に、sjisに変換しています。
機種依存文字などが含まれる場合は、sjis-win あたりがの方が良いかと思います。
これで、http://your-domain.com/users/download にアクセスすれば、設定したファイル名のCSVファイルをダウンロードします。
余談
本当は、この機能を管理画面(/admin/users/download)で使いたかったのですが、プレフィックスルーティンで管理画面を制御していたために、コントローラー側でレイアウトを無効としても、無効にならず、管理画面用のレイアウトファイルをcsvファイルとして出力するという事態に陥ってしまい、1時間ほど「あーでもないこーでもない」とやってました。
社内に置いてあるローカルサーバでしか運用しないので、(管理画面内ではなく)フロント側のURLとしても問題なかったのですが、プレフィックスルーティンを使っている場合、(ダウンロード機能の場合だけはプレフィックスルーティンを)無効にする、といった手段を講じる必要がありそうです。
とりあえずこれは未来の私へのメッセージ。
絶対に同じことで悩んでそうなので(苦笑)
CakePHP2.x系統での場合もまた記事にしたいと思います。