问题场景:
tp的大版本为5.0
模型之间是一对一的join形式关联,不附加筛选条件的情况下查询是正常的,但是一旦附加了时间范围查询就报错了,报:表不存在错误
问题排查:
经过排查发现是因为使用了tp的between time导致的。
源码分析:
经过分析tp5的框架源码,发现具体原因出在:thinkphp/library/think/db/Builder.php的parseDateTime方法中,parseDateTime单纯靠list($table, $key) = explode('.', $key);
来获取表名,而多表join关联查询时,这样是获取不到正确的表名的
以下为tp5中截取的parseDateTime方法源码:
/**
* 日期时间条件解析
* @access protected
* @param string $value
* @param string $key
* @param array $options
* @param string $bindName
* @param integer $bindType
* @return string
*/
protected function parseDateTime($value, $key, $options = [], $bindName = null, $bindType = null)
{
// 获取时间字段类型
if (strpos($key, '.')) {
list($table, $key) = explode('.', $key);
if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
$table = $pos;
}
} else {
$table = $options['table'];
}
$type = $this->query->getTableInfo($table, 'type');
if (isset($type[$key])) {
$info = $type[$key];
}
if (isset($info)) {
if (is_string($value)) {
$value = strtotime($value) ?: $value;
}
if (preg_match('/(datetime|timestamp)/is', $info)) {
// 日期及时间戳类型
$value = date('Y-m-d H:i:s', $value);
} elseif (preg_match('/(date)/is', $info)) {
// 日期及时间戳类型
$value = date('Y-m-d', $value);
}
}
$bindName = $bindName ?: $key;
if ($this->query->isBind($bindName)) {
$bindName .= '_' . str_replace('.', '_', uniqid('', true));
}
$this->query->bind($bindName, $value, $bindType);
return ':' . $bindName;
}
解决方案:
目前不知道这个是tp5的bug还是设计上就是这样子的,但是可以从源码中发现查询逻辑为:between time,’< TIME’, ‘> TIME’, ‘<= TIME’, ‘>= TIME’ 时tp5在查询条件组装时就会导致找不到正确表名。所以目前只能尽量避免使用这种查询方式,时间查询时尽量用大于小于号或者between
ps:度娘了一下,发现也有网友说tp5.1同样存在一样的问题,所以用tp5.1的同学也要注意了喔