最近在使用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
- bug的表现为parsestr 会对
.
进行处理将之替换为``; - 产生的位置是
_query
方式 - 产生必备条件使用了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中有这样的设置,我现在还没有仔细去看,等细看后会回来补充。