こんにちは、小川です。
今週はsymfony 1.2がリリースされ、Jobeetという新たなチュートリアルが始まったりとsymfony界隈がとても盛り上がっていますね。symfonyに力を入れてるアシアルとしてもうれしい限りです。
さて、symfony 1.2にデフォルトで含まれているDoctrine。そのDoctrineでデータベースからオブジェクトを取得するにはDoctrine_Queryというオブジェクトを使いますが、実はこのDoctrine_Queryはサブクエリにも対応しています。今回はそのサブクエリの使い方をご紹介したいと思います。
今回使うスキーマは以下になります。
User:
actAs:
Timestampable:
columns:
id:
type: integer(4)
primary: true
notnull: true
autoincrement: true
email:
type: string(255)
notnull: true
password:
type: string(50)
notnull: true
Order:
actAs:
Timestampable:
columns:
id:
type: integer(4)
primary: true
notnull: true
autoincrement: true
user_id:
type: integer(4)
notnull: true
total:
type: integer(4)
notnull: true
relations:
User:
foreignAlias: Orders
では、早速サブクエリを使ってオブジェクトを取得してみましょう。
以下は購入平均額が3000円を超えている優良ユーザをサブクエリを用いて取得する例です。
<?php
class UserTable extends Doctrine_Table
{
public function getPrimeUsers($threshold = 3000)
{
$q = $this->createQuery('u');
// サブクエリを作成
$q2 = $q->createSubquery()->select('o.user_id')
->from('Order o')
->groupBy('o.user_id')
->having('AVG(o.total) >= ?', $threshold);
//echo $q2->getDql();
//=> SELECT o.user_id FROM Order o
// GROUP BY o.user_id HAVING AVG(o.total) >= ?
// サブクエリをメインのクエリに入れる
$q->where("u.id IN ({$q2->getDql()})");
return $q->execute();
}
}
$primeUsers = Doctrine::getTable('User')->getPrimeUsers();
まずcreateQuery()で基準となるDoctrine_Queryオブジェクトを生成します。
そのオブジェクトにcreateSubquery()というメッセージを送ると、もう1つDoctrine_Queryオブジェクトが生成されます。このオブジェクトは内部でサブクエリフラグを持っており、このオブジェクトに対してサブクエリの条件をつけていきます。
そしてこのサブクエリを適応する方法は至って単純です。getDql()メソッドで文字列にして、where文の中にいれるだけです。ただし、ここでプレースホルダを使用するとシングルコーテーションで囲まれてクエリとして認識されないので、直接文字列の内部に入れてください。
ちなみにサブクエリを用いるときに、SELECT句やFROM句の中にもサブクエリを使いたい場合があると思います。少し試してみただけなのですが、SELECT句にはサブクエリを使えるものの、FROM句の中では使えないようです。というのも、どうやらDQLでは、FROM句には必ず実在するクラス名を指定する必要があるようです。FROM句にサブクエリを使いたい場合は素直にSQLを書きましょう。
symfony 1.2にはDoctrineが最初から含まれていますので、今までPropelを使っていた方々もぜひ使ってみてください。今後もsymfonyやDoctrineに関する様々な情報やノウハウを配信していく予定ですので、どうぞよろしくお願いいたします。