ThinkPHP中使用hasWhere时field失效的问题
ThinkPHP中使用hasWhere时field失效的问题,从TP5开始就有人在讨论,如下代码:
$data = Project::Field('project.id,c_id,year')
->hasWhere('catalog', function ($query) {
$query->where('name', 'like', '%测试%');
})->select();
当使用hasWhere时,field对字段的过滤会失效,也就是会查询出project中的所有字段,但是field中定义的字段别名却是有效的,也就是说,在ThinkPHP中当使用hasWhere时field会半失效(不会过滤字段,会查询出所有字段,但对字段所做的别名会生效)。
点击查看hasWhere的源代码后,我们发现,原来hasWhere方法的参数,不止我们常用的2个,它还有很多我们不常用的参数。
经查看,该方法一共有五个参数,第三个参数 fields即为对字段的过滤,且默认值为星号*,也就是全部显示。因此这也就能解释我们在field中所做的过滤不会生效,但是字段别名却能生效的原因了。
即,正确的写法应该是将需要显示的字段放置在hasWhere方法的第三个参数中,如下:
$data = Project::hasWhere('catalog',
function ($query) {
$query->where('name', 'like', '%测试%');
}, 'id,c_id,build')
->select();
另外,在使用field+hasWhere组合时,我们需要给所有两个模型中名称相同的字段加上表名前缀或模型名前缀,而在将field做为hasWhere第三个参数使用时,则无需添加表名前缀。
注意点一:
需要注意的是,hasWhere的第三个参数,必须是字符串类型,而且不能是太复杂的类型 ,比如:
hasWhere('catalog', $catalogMap, "id,catalog_id, center_cz as ttt,task") // 支持
hasWhere('catalog', $catalogMap, "id,catalog_id, (center_jj + prov_jj) as ttt,task") // 不支持
解决的方式有两种:
一种是:field+hasWhere一起使用,最后查看调试器中的SQL会发现ThinkPHP对字段进行了合并:
写法如下:
->field('(center + prov) AS total')
->hasWhere('catalog', $map, "id,catalog_id,task")
最后生成的sql查询语句为:
SELECT (center + prov) AS total,`id`,`catalog_id`,`task` FROM `xxx`
还有一种更加简单的方式,是测试的时候测试出来的,即:去掉外面的括号,代码如下:
->hasWhere('catalog', $map, "id,catalog_id,center_cz + center_jj AS total,task")
注意点二:
在我们使用获取器定义了一些虚拟字段时,以往通过field+append的方式就可以获取到,但是当我们把字段信息放在hasWhere的第三个参数中时,此时,如果append出现在hasWhere之前,则无效,需要将append放置在hasWhere之后才行,代码如下:
//错误的
$builder = $model->append(['task_text', 'status_text'])->hasWhere('catalog', $catalogMap, $field)
// 正确的
$builder = $model->hasWhere('catalog', $catalogMap, $field)->append(['task_text', 'status_text'])
关于其他一些使用hasWhere时的坑点,我们之前也有总结过,详见:《记录一些ThinkPHP模型关联查询中hasWhere的一些坑点总结》