Nested where clause on Zend_Db_Select
Zend Framework: 1.11.0
PHP: 5.3.3
目的
権限や表示属性を確認しつつ検索するWHERE句を持つSQLをZend_Db_Selectを使って作る。
簡単に書くと、以下の構造を持つSQLを作りたい。
SELECT * FROM table WHERE ( (データへのアクセス権チェック) AND (データのチェック) ) AND ( (検索条件) )
結論
追記:
$where_permit = array(); $where_permit[] = $this->_db->quoteInto('g=?', 1); $where_permit[] = $this->_db->quoteInto('o=:id'); $where_permit = implode(' AND ', $where_permit); $where_data = array(); // 略 $where_search = array(); // 略 $where_search = implode(' OR ', $where_search); $select->where($where_permit)->where($where_data)->where($where_search);
これが好きかも。
SQL文を書きましょう。
$select->where( '(' . '(データへのアクセス権チェック)' . ' AND ' . '(データのチェック)' . ')' ); $select->where( '(' . '(検索条件)' . ')' );
ダサすぎる。
いつでも$select->where('条件','値')を呼び出せるのが利点なのに、
自作のWHERE句管理機構を作って最後に流し込むようなもの。
これではエスケープしか利点がないと思う。
Zend_Db_Selectを見る
公式マニュアル - http://framework.zend.com/manual/en/zend.db.select.html
$select->where()が呼び出されると以下のように続いていくが、
結局はZend_Db_Select._parts[self::WHERE][]に'key = value'の形で追加されてゆく。
$select->whereの道筋
1. Zend_Db_Select.where()
2. Zend_Db_Select._where()
3. Zend_Db_Adapter_Abstract.quoteInto()
4. Zend_Db_Adapter_Abstract.quote()
(以下略)
Zend/Db/Select.php
/** * Adds a WHERE condition to the query by AND. * * If a value is passed as the second param, it will be quoted * and replaced into the condition wherever a question-mark * appears. Array values are quoted and comma-separated. * * <code> * // simplest but non-secure * $select->where("id = $id"); * * // secure (ID is quoted but matched anyway) * $select->where('id = ?', $id); * * // alternatively, with named binding * $select->where('id = :id'); * </code> * * Note that it is more correct to use named bindings in your * queries for values other than strings. When you use named * bindings, don't forget to pass the values when actually * making a query: * * <code> * $db->fetchAll($select, array('id' => 5)); * </code> * * @param string $cond The WHERE condition. * @param mixed $value OPTIONAL The value to quote into the condition. * @param int $type OPTIONAL The type of the given value * @return Zend_Db_Select This Zend_Db_Select object. */ public function where($cond, $value = null, $type = null) { $this->_parts[self::WHERE][] = $this->_where($cond, $value, $type, true); return $this; }
追加されてゆくけれど、残念ながら以下のようには書けません。
$where_permit = $db->select()->where('権限確認'); $where_data = $db->select()->where('データ確認'); $where_search = $db->select()->where('検索条件'); $select->where( '(' . (string)$where_permit . ' AND ' . (string)$where_data . ') AND (' . (string)$where_search . ')' )
Zend_Db_Selectは__toString()でSQLを出力できるけれど、
その過程でFROM句が入っていないとWHERE句を出力しないように
してあります。
また、名前の通りSELECTが容赦なく文字列の先頭に挿入されます。
(string)$selectの道筋
1. Zend_Db_Select.__toString()
2. Zend_Db_Select.assemble()
3. Zend_Db_Select._renderWhere()
※ _renderClause は Zend_Db_Select::_partsInit 順に実行される
Zend/Db/Select.php
/** * Render WHERE clause * * @param string $sql SQL query * @return string */ protected function _renderWhere($sql) { if ($this->_parts[self::FROM] && $this->_parts[self::WHERE]) { $sql .= ' ' . self::SQL_WHERE . ' ' . implode(' ', $this->_parts[self::WHERE]); } return $sql; }
注意しなくてはいけないのは
たとえば以下のようなコードはだめ。
$select = $db->select()->from($table); $select->where('権限確認'); $select->where('データ確認'); if (operation == 'AND') { $select()->where('検索条件'); } else { $select()->orWhere('検索条件'); } echo (string)$select; >> SELECT * FROM $table WHERE (権限確認) AND (データ確認) OR (検索条件)
この順番だからかろうじて権限は守られているけれど、
(データ確認) AND (権限確認) OR (検索条件) とかやったら。
怖すぎる。
そもそもorWhereに需要はあるのだろうか。
A=? AND B=? OR C=? AND D=? とか考えるだけでいやになる。
まとめ
これを見てる企業の人、本家にその拡張を提案してください。
ぼやき
しかし、このフレームワークを使っている人たちは
みんなどうやってるんだろうか。
こういった処理は特殊じゃなくて確実に発生すると思うのだが。