那須です
PHPフレームワークの1つSymfonyでは、PropelというORマッパを使って、データベースにアクセスすることが多いと思います。ただ、どうしても複雑な検索結果がほしい場合、自分でSQLを書く必要があります(書かなくてもなんとかなったりしますが、SQL書いた方がすっきりします)。
ただ、自分でSQLを書くとその結果はPropelのオブジェクトとしては扱えません。そこで、今回は自分で書いたSQLからPropelのオブジェクトを生成する方法を整理しておきます。
以前、Propelお役立ちサイトの中に自分で書いた SQL から populated Object を作るなどにもまとめられていますので、そちらも参照してください。
例として、次のような構成のテーブルがあり外部結合しているとします。
- Userテーブル
- UserAttributesテーブル
ここで、UserAttributesテーブルのデータを条件として、Userテーブルの情報が欲しいとします。この例は簡単なので、UserAttributesテーブルとUserテーブルをaddJoin()してもいいと思いますが、これがさらに入り組んでくるとPropelで表現するのはかなり複雑になります。そこで直接SQLを書くと次のようなコードになります。
$con = Propel::getConnection(UserPeer::DATABASE_NAME);
$field_names = UserPeer::getFieldNames(BasePeer::TYPE_FIELDNAME);
// 2008年8月以降にログインしたユーザを取得する
$sql = sprintf(”
SELECT %s
FROM %s u, %s ua
WHERE ua.last_logon >= ‘2008-08-01′
AND u.id = ua.user_id
”
, join(‘,’, array_map(create_function(‘$v’, ‘return “u.” . $val;’), $field_names))
, UserPeer::TABLE_NAME
, UserAttributesPeer::TABLE_NAME
);
$stmt = $con->prepareStatement($sql);
$rs = $stmt->executeQuery(ResultSet::FETCHMODE_NUM);
$users = UserPeer::populateObjects($rs);
foreach ($users as $user) {
echo $user->getName();
}
ここで肝となるのは
$field_names = UserPeer::getFieldNames(BasePeer::TYPE_FIELDNAME);
と
join(‘,’, array_map(create_function(‘$v’, ‘return “u.” . $val;’), $field_names))
です。普通に、u.* でもいいのですが、これだとカラムの順番がUserオブジェクト内で定義されている順番と異なってしまい、期待しない動作をすることがあります。このようにしておくことで、DB上の定義とPropel上での定義の違いを気にすることなく、Userオブジェクトを作ることができます。
ちなみにこの方法は、UserAttributeテーブルの情報もまとめて取得したいと言うときやグルーピングして集計結果を取得したいというときには役立ちません。Userオブジェクトには定義されていないので当然と言えば当然ですが。そのときは、自分でModelを用意するか、viewを作ってそのviewをbuild modelするのがいいでしょう。