今回は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このSQLをphpMyAdminなどで直接実行してみると、データベースフィールドの最後にtotalとamountフィールドが追加されたはずだ。
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');最初のメソッドであるaddGridだが、引数をいくつか指定することが可能になっている。
以下のように2番目の引数にfalse を指定すると、項目名が表示されなくなる。
$csv->addGrid($histories, false);これで第1の条件はクリアした。
次に、任意の文字列を表示させる。
任意の文字列は配列で、以下の様な内容だ。
$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 ) ) )なんと言う事か、 item_id、name、priceと兄弟要素として並んでいると思ったamountとtotalが、別の親をインデックスに持つ配列になってしまっている。この場合、[0][amount]、[0][total]だ。
これは非常に迷惑な仕様だ。
先ほどの完成形のままだと、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])); }これで、$datasをビューに渡せば、以下の様な配列がわたることになる。
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