PropelでSQLを書くときのヒント
2008年09月22日(月)13:37|那須
那須です
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するのがいいでしょう。