今回はCSVファイルのカスタマイズなので、ここまでで終わりではない。
このようなCSVファイルを
id | item_id | name | price | created |
---|---|---|---|---|
1 | 1 | あきたこまち | 1980 | 2010-01-01 24:59:59 |
2 | 2 | ささにしき | 2034 | 2010-01-01 24:59:59 |
3 | 3 | こしひかり | 1680 | 2010-01-01 24:59:59 |
4 | 1 | あきたこまち | 1980 | 2010-01-01 24:59:59 |
5 | 3 | こしひかり | 1680 | 2010-01-01 24:59:59 |
このようにしたいわけだ。
ID | 商品名 | 単価 | 個数 | 合計額 |
---|---|---|---|---|
1 | あきたこまち | 1980 | 2 | 3,960円 |
2 | ささにしき | 2034 | 1 | 2,034円 |
3 | こしひかり | 1680 | 2 | 3,360円 |
それではやってみよう。
■コントローラのカスタマイズ
まずはコントローラ側で、各商品ごとに集計させるところからはじめる。
基本的には$conditions配列に条件を書けばいいが、まずは素のSQLを指定してみる。
※MySQLを使用
- select *, sum(price) as total , count(item_id) as amount
- from histories as History
- group by History.item_id
totalには集計された金額、amountには商品の個数が設定されている。
id | item_id | name | price | created | total | amount |
---|---|---|---|---|---|---|
1 | 1 | あきたこまち | 1980 | 2010-01-01 23:59:59 | 3960 | 2 |
2 | 2 | ささにしき | 2034 | 2010-01-01 23:59:59 | 2034 | 1 |
3 | 3 | こしひかり | 1680 | 2010-01-01 23:59:59 | 3360 | 2 |
この中から、item_id、name、price、total、amountを使用すればよい。
つまり、コントローラ側としては任意のフィールドを指定してfindすればよい、と言うことになる。
$conditions にはfirldsという設定があるので、これでフィールド名を設定してみる。
- $conditions = array(
- 'conditions' => array(
- 'History.created >' => '2010-01-01 23:59:59',
- ) ,
- 'fields' => array(
- 'History.item_id',
- 'History.name',
- 'History.price',
- 'count(History.item_id) as amount',
- 'sum(History.price) as total',
- ),
- 'group' => array(
- 'History.item_id',
- ),
- );
- $this->set('histories', $this->History->find('all', $conditions));
Id | Name | Price | Amount | Total |
---|---|---|---|---|
1 | あきたこまち | 1980 | 2 | 3960 |
2 | ささにしき | 2034 | 1 | 2034 |
3 | こしひかり | 1680 | 2 | 3360 |
これで取得したいデータはそろった。
次は項目名を任意の文字列に変更する。
■ビューのカスタマイズ
現在ビュースクリプトであるcsv.ctpには、細かい設定などが一切されてない状態になっている。
しかしCSVヘルパーにはいくつか便利な機能が含まれているので、それらを使ってカスタマイズすることにする。
まず、項目名はデータベースフィールドのCamelCase方式で表示されてしまっているので、これらを任意の文字列にするときのアルゴリズムを考える。
ちょっとひねった考え方をすると、データベースフィールド名と任意の文字列をセットした、連想配列にして出力すればよいと思われがちだが、もっと簡単に出来る方法がある。
- 項目名を非表示設定にする
- 1行目を任意の配列として挿入する
というわけで、まずは項目名を非表示にする設定をする。
[/Path/To/CakePHP/App/views/histories/csv.ctp]
- $csv->addGrid($histories);
- $csv->setFilename('支払.csv');
- echo mb_convert_encoding($csv->render(), 'SJIS', 'UTF-8');
以下のように2番目の引数にfalse を指定すると、項目名が表示されなくなる。
- $csv->addGrid($histories, false);
次に、任意の文字列を表示させる。
任意の文字列は配列で、以下の様な内容だ。
- $columns = array(
- 'ID' ,
- '商品名',
- '単価',
- '個数',
- '合計額',
- );
CSVヘルパーでは、全データはaddGridが呼ばれて初めてデータがそろう仕組みになっている。
つまり、addGridを呼ぶ前に、先頭に1行だけ追加できれば良い。
そのためのメソッドが用意されている。そのまんまの名前で、addRowだ。これをaddGridの前に追加する。
- $csv->addRow($columns);
最終的にcsv.ctpは以下のようになったはずだ。
[/Path/To/CakePHP/App/views/histories/csv.ctp]
- $columns = array(
- 'ID',
- '商品名',
- '単価',
- '個数',
- '合計額',
- );
- $csv->addRow($columns);
- $csv->addGrid($histories, false);
- $csv->setFilename('支払.csv');
- echo mb_convert_encoding($csv->render(), 'SJIS', 'UTF-8');
しかしこれだけでは終わらない。
■コントローラのカスタマイズ
実はfindで取得したデータをダンプしてみると、非常に厄介な構造になっているのが分かると思う。
ためしにcsvアクションを、次のように編集してみると良い。
- //Configure::write('debug', 0); // デバッグ情報不要
- //$this->layout = false; // layout 不要
- $conditions = array(
- 'conditions' => array(
- 'created <' => ’2010-01-01’ 23:59:59',
- ),
- 'fields' => array(
- 'History.item_id',
- 'History.name',
- 'History.price',
- 'count(History.item_id) as amount',
- 'sum(History.price) as total',
- ),
- 'group' => array(
- 'History.item_id',
- ),
- );
- $histories = $this->History->find('all', $conditions);
- pr($histories);exit(0);
- //$this->set('histories', $this->History->find('all', $conditions));
最初の2行、最後の1行をコメントアウトし、findした内容をprで画面にダンプする設定だ。
これでブラウザでcsvアクションの実行結果を見ることが出来る。
ためしに見てみると、
- Array
- (
- [0] => Array
- (
- [History] => Array
- (
- [item_id] => 1
- [name] => あきたこまち
- [price] => 1980
- )
- [0] => Array
- (
- [amount] => 2
- [total] => 3960
- )
- )
- [1] => Array
- (
- [History] => Array
- (
- [item_id] => 2
- [name] => ささにしき
- [price] => 2034
- )
- [0] => Array
- (
- [amount] => 1
- [total] => 2034
- )
- )
- [2] => Array
- (
- [History] => Array
- (
- [item_id] => 3
- [name] => こしひかり
- [price] => 1680
- )
- [0] => Array
- (
- [amount] => 2
- [total] => 3360
- )
- )
- )
これは非常に迷惑な仕様だ。
先ほどの完成形のままだと、amountとtotalがnullになってcsvファイルに吐き出されてしまう。
なので[History][amount]と[History][total]というようになってないとまずい。
どうにかしないといけない。
ここら辺はビュースクリプトでやるのはおかしいので、コントローラでどうにかして兄弟にしておくこととする。
非常に遺憾なロジックだが、一度配列に代入した全データを総なめし、配列に入れなおすことにする。
- $histories= $this->History->find('all', $conditions);
- for($i=0; $i<count($histories);$i++) {
- $datas[$i]['History'] = am($histories[$i]['History'], ($histories[$i][0]));
- }
- Array
- (
- [0] => Array
- (
- [History] => Array
- (
- [item_id] => 1
- [name] => あきたこまち
- [price] => 1980
- [amount] => 2
- [total] => 3960
- )
- )
- [1] => Array
- (
- [History] => Array
- (
- [item_id] => 2
- [name] => ささにしき
- [price] => 2034
- [amount] => 1
- [total] => 2034
- )
- )
- [2] => Array
- (
- [History] => Array
- (
- [item_id] => 3
- [name] => こしひかり
- [price] => 1680
- [amount] => 2
- [total] => 3360
- )
- )
- )
最終的に出来上がったcsvアクションは以下になる。
[/Path/To/CakePHP/App/controllers/histories_controller.php]
- function csv()
- {
- Configure::write('debug', 0); // デバッグ情報不要
- $this->layout = false; // layout 不要
- $conditions = array(
- 'conditions' => array(
- 'created <' => '2010-01-01 23:59:59',
- ),
- 'fields' => array(
- 'History.item_id',
- 'History.name',
- 'History.price',
- 'count(History.item_id) as amount',
- 'sum(History.price) as total',
- ),
- 'group' => array(
- 'History.item_id',
- ),
- );
- $histories = $this->History->find('all', $conditions);
- for($i=0; $i<count($histories);$i++) {
- $datas[$i]['History'] = am($histories[$i]['History'],
- $histories[$i][0]
- );
- }
- $this->set('histories', $datas);
- }
結構な面倒くささだ。
ここら辺まとめてコンポーネントにしておくと便利かもしれない。
とにかくお試しあれ。
じゃぁな。
facebook
twitter
google+
fb share