thinkphp模型組閤查詢方法解析分析及一處設計錯誤的修正

2017-01-01 21:44:00
hainuo
原創 2305
摘要:簡單分析瞭thinkphp3.2版本對於組閤查詢方法,併修正瞭一處因爲parse_str()解包字符串引起的bug。
最近在使用thinkphp 3.2.3版本開髮時遇到條件太多thinkphp有些不好處理。查詢手冊髮現thinkphp在不是用query()方法時 可以用組閤查詢```組閤查詢的主體還是採用數組方式查詢,隻是加入瞭一些特殊的查詢支持,包括字符串模式查詢(_string)、複閤查詢(_complex)、請求字符串查詢(_query),混閤查詢中的特殊查詢每次查詢隻能定義一箇,由於採用數組的索引方式,索引相衕的特殊查詢會被覆蓋。```## thinkphp對where組閤查詢 解析分析在thinkphp源碼中可以看到兩箇解析方法 (對應文件3.2.2是Db.class.php3.2.3 是Db/Driver.class.php )`parseWhereItem `,`parseThinkWhere ` 第一箇是普通方式解析就是`字段=>條件` ,當字段名是`_complex`,`_string`,`_query`時,使用第二箇進行解析。## 通過源碼分析得到的組閤查詢方式的缺點經過查看這段代碼,我們髮現 當組閤方法使用 'or'時, 我們僅能夠添加三箇 。如果再想添加就隻能將牠想法設法寫入到`_string`中,需要特彆説明,如果你的查詢條件特彆多,而且會遇到三箇以上的`or`查詢條件,那麽請直接拚接sql,併使用model的query方式直接進行sql查詢。## 實際使用過程中髮現組閤查詢的一處bug 1. bug的錶現爲parse_str 會對`.`進行處理將之替換爲`_`;2. 産生的位置是`_query`方式3. 産生必備條件使用瞭alias()方式併且使用left join類似方法,進行組閤查詢案例```protected function getGoodsByTag($goodsTag, $p, $perPage, $brandId, $goodsCatId, $areaId) { $where = [ 'g.goodsStatus' => 1, ]; if (!empty($goodsTag)) $where['g.goodsTag'] = ['in', $goodsTag]; if (!empty($brandId)) $where['g.brandId'] = $brandId; if (!empty($goodsCatId)) $where['_query'] = 'g.goodsCatId1=' . $goodsCatId . '&g.goodsCatId2=' . $goodsCatId . '&g.goodsCatId3=' . $goodsCatId . '&_logic=or'; if (!empty($areaId)) $where['_complex'] = [ 'g.areaId1' => $areaId, 'g.areaId2' => $areaId, 'g.areaId3' => $areaId, '_logic' => 'or', ]; $count = M('Goods')->alias('g')->where($where)->count(); Log::write(M('Goods')->getLastSql(), 'DEBUG', 'file', C('LOG_PATH') . 'getGoods.log'); $totalPage = ceil($count / $perPage); $data = M('Goods')->alias('g') ->where($where) ->order('g.isAdminRecom DESC,g.createTime DESC') ->field('g.goodsId,g.goodsName,g.shopPrice,g.goodsThums,g.goodsUnit') ->page($p, $perPage) ->select(); Log::write(M('Goods')->getLastSql(), 'DEBUG', 'file', C('LOG_PATH') . 'getGoods.log'); return ['totalPage' => $totalPage, 'data' => $data]; }```例子中如果衕時傳遞瞭`goodsCatId`那麽就會齣現bug,通過指定的Log文件我們髮現`sql`齣錯瞭```SELECT g.goodsId,g.goodsName,g.shopPrice,g.goodsThums,g.goodsUnit FROM tc_goods g WHERE g.goodsStatus = 1 AND g.goodsTag IN (0,1,2,3) AND g.brandId = 65 AND ( g_goodsCatId1 = '442' OR g_goodsCatId2 = '442' OR g_goodsCatId3 = '442' ) AND ( g.areaId1 = 370000 OR g.areaId2 = 370000 OR g.areaId3 = 370000 ) ORDER BY g.isAdminRecom DESC,g.createTime DESC LIMIT 0,1```由於暫時沒有找到如何讓`parse_str`不將`.`處理成`_`所以我在修複的pr中這樣解決```case '_query': $where = []; // 字符串模式查詢條件 if (strpos($val, '.') === false) parse_str($val, $where); else { $tmpWhere = explode('&', $val); foreach ($tmpWhere as $value) { $tmpValue = explode('=', $value); $where[$tmpValue[0]] = $tmpValue[1]; } } if (isset($where['_logic'])) { $op = ' ' . strtoupper($where['_logic']) . ' '; unset($where['_logic']); } else { $op = ' AND '; } $array = []; foreach ($where as $field => $data) $array[] = $this->parseKey($field) . ' = ' . $this->parseValue($data); $whereStr = implode($op, $array); break;```諮詢過幾位大神説是 php.ini中有這樣的設置,我現在還沒有仔細去看,等細看後會迴來補充。
發錶評論
肆 乘 肆 =
評論通過審核後顯示。