virtualField機能使ってみた

shin1x1さんがtwitterで「ver1.3にこんな機能がある」とtwitterで呟かれていたのをみて、実際に使ってみました。


virtualField機能で、できること*1

  • 定形の計算や文字列処理を、取得時にSQL内で行う
    • SQL関数を直接指定


まずは、作成したMemberモデルに、元記事を参考に、$virtualFieldを設定して見ます*2

app/model/members.php

var $virtualFields = array(
		'name' => 'CONCAT(Member.first_name, " ", Member.family_name)'
);

そして、bakeで作ったindex(members一覧)にアクセスすると、こんなSQLが出力されていました。

SELECT `Member`.`id`, `Member`.`first_name`, `Member`.`family_name`, `Member`.`created`, `Member`.`modified`, (CONCAT(`Member`.`first_name`, " ", `Member`.`family_name`)) AS `Member__name` FROM `members` AS `Member` WHERE 1 = 1 LIMIT 20


不覚なことに、私、SQLはあまり複雑な使い方はしてこなかったため、「CONCAT」という表現にピンときてませんでした。
これ、SQLの文字列関数です(゚ロ゚;)


つまり、virtualFieldを使うと、
SELECTのフィールド指定部分に、いろいろ自由にセットできるということ?


試しに、違う関数を入れてみる。

var $virtualFields = array(
	'name' => 'CONCAT(Member.family_name, " ", Member.first_name)',
	'date' => 'DATE(Member.created)' // 追加
);

そして、再度index()にアクセス。

SELECT `Member`.`id`, `Member`.`first_name`, `Member`.`family_name`, `Member`.`created`, `Member`.`modified`, (CONCAT(`Member`.`family_name`, " ", `Member`.`first_name`)) AS `Member__name`, (DATE(`Member`.`created`)) AS `Member__date` FROM `members` AS `Member` WHERE 1 = 1 LIMIT 20

結果をvar_dumpで出力してみる。

array(1) {
[0]=> array(1)
{ ["Member"]=> array(7)
{ ["id"]=> string(1) "1"
(中略)
["name"]=> string(13) "坂東 太郎"
["date"]=> string(10) "2010-01-20"

おおー ^^

AS `Member__name`

とダブルアンダースコアで参照されてたので変数名心配だったんですが、気にしなくてOK。


さらに試しに、membersテーブルに、「money(所持金)」なんてカラムを追加して、数値計算とかしてみる。

mysql> ALTER TABLE members ADD COLUMN money INT(11) NOT NULL default '0';
Query OK, 1 row affected (0.05 sec)
Records: 1  Duplicates: 0  Warnings: 0
mysql> quit
Bye
[cake@cake app]$ cd ../
[cake@cake cakephp-13b]$ vi app/views/members/index.ctp

(以下略、indexとadd,editにnameとmoneyの表示追加)

一覧に、nameとmoneyがでるようになったところで、データ追加。
そして、同じfamily_nameのメンバーのmoneyを合算して一覧表示するアクションを作成。

app/controller/members_controller.php

function grouped_list() {
        $this->Member->virtualFields['money_sum'] = 'SUM(Member.Money)';
        $list = $this->Member->find('all', array(
                        'group' => 'Member.family_name',
                )
        );
        $this->set('members', $list);
}

できました! (^^)


全メンバー一覧

family_nameごとに合算


フィールドに自由にセットできるので、使い方次第でいろんなことが出来そうです。

*1:もっとあるかもしれない・・・

*2:実は元記事、そのままだとSyntax Error ^^; なので注意