Zend_Db_Table の join でハマる
Zend_Db_Table で join を使って、テーブルを結合してデータを取ろうとしてハマった。
発行したい SQL はこんな感じ。
SELECT statuses.id, statuses.user_id, statuses.reply_user_id, statuses.comment, statuses.created_at, users.user_name, users.private_flag FROM statuses JOIN users ON users.id = statuses.user_id WHERE statuses.user_id = ? AND users.private_flag = ?
users テーブルと、statues テーブルを id と user_id で join する至って普通なもの。
O/R マッパとして Zend_Db_Table_Abstract を使用している。
<?php Class Statuses extends extends Zend_Db_Table_Abstract { protected $_name = 'statuses'; protected $_primary = 'id'; }
で、呼び出しもとで、ドキュメント(http://framework.zend.com/manual/ja/zend.db.select.html#zend.db.select.building.join)に記述されているを参考に作った。
<?php // $this->_dao は Statues クラスのオブジェクト $select = $this->_dao->select() ->from(array('s' => 'statuses')) ->join(array('u' => 'users'), 'u.id = s.user_id', array('u.user_name', 'u.private_flag')) ->where('s.user_id = ?', $id); ->where('u.private_flag = ?', 0); $rows = $this->_userDao->fetchAll($select)
上記を実行してみると、エラーになった。
SQLSTATE[42000]: Syntax error or access violation: 1065 Query was empty
Zend_Db_Select には __toString() というメソッドがあり、それを使用すると、SQL 文に変換してくれる。
<?php var_dump($select->__toString());
これを実行してみると、以下のようなワーニングが出た。
Warning: Select query cannot join with another table in
ドキュメントで、連結するテーブルのカラム名を指定しない場合の記述方法があったので、そちらを試してみたら、
ちゃんと正しく実行が出来る(ただし当然連結するテーブルのカラムの値は取れない)。
この現象がどうにも謎で検索してみたが、Zend_Db_Table で join を使っている良いサンプルや事例が見つける事が出来なかった。
で、zf-users.jp の IRC で聞いてみたら、id:wozozo さんが、
http://www.zfforums.com/zend-framework-components-13/databases-20/problems-join-672.html この症状と同じじゃない?
って見つけてくれた。
読んでみると、まさしく同一の現象。
で、レスの中に、
hi, i was told you need to set setIntegrityCheck to false
http://www.zfforums.com/zend-framework-components-13/databases-20/problems-join-672.html
とあったので、試してみた。
<?php // $this->_dao は Statues クラスのオブジェクト $select = $this->_dao->select()->setIntegrityCheck(false) ->from(array('s' => 'statuses')) ->join(array('u' => 'users'), 'u.id = s.user_id', array('u.user_name', 'u.private_flag')) ->where('s.user_id = ?', $id); ->where('u.private_flag = ?', 0); $rows = $this->_userDao->fetchAll($select)
エラーにならずに、無事にデータを取る事が出来た!
この setIntegrityCheck(false) は何者かというと、ドキュメントに以下のように書いてあった。
Zend_Db_Table_Select の主な使用目的は、 制約を強要して正しい形式の SELECT クエリを作成することです。 しかし時には、Zend_Db_Table_Row の柔軟性が必要であって 行を更新したり削除したりすることはないということもあります。 そんな場合には、setIntegrityCheck に false を渡して行/行セットを取得することができます。 この場合に返される行/行セットは 'ロックされた' 行 (save()、delete() やフィールドの設定用メソッドを実行すると例外が発生する) となります。
http://framework.zend.com/manual/ja/zend.db.table.html#zend.db.table.fetch-all
制約を強制しているから、エラーとなっているよう。上記のドキュメントに書かれている通り、行を更新、削除をせず、
データを取得したいだけなので、 setIntegrityCheck(false) を指定しても問題なさそう。
# というか指定しないと取得できない。
自分一人では解決が難しいときに聞ける場所があるのは凄くありがたい。
ということで、何か分からない事があったり、情報を提供して貰えるのなら IRC の #zftalk-ja@freenode に参加すると、みんな幸せになれると思う。