问题描述
tp6 的远程一对一关联查询 当要查询的关联数据查询时会出现相同不是为null 的情况。
可能这个用的人比较少的原因,网上对hasOneThrough的相关资料比较少,出现这个问题就记录一下。
原因分析
下面是hasOneThrough的源码
```php
/**
* HAS ONE 远程关联定义
* @access public
* @param string $model 模型名
* @param string $through 中间模型名
* @param string $foreignKey 关联外键
* @param string $throughKey 关联外键
* @param string $localKey 当前主键
* @param string $throughPk 中间表主键
* @return HasOneThrough
*/
public function hasOneThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = '', string $throughPk = ''): HasOneThrough
{
// 记录当前关联信息
$model = $this->parseModel($model);
$through = $this->parseModel($through);
$localKey = $localKey ?: $this->getPk();
$foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
$throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName());
$throughPk = $throughPk ?: (new $through)->getPk();
return new HasOneThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $throughPk);
}
我这里用的是VsCode,按住点击Crtrl 和 鼠标点击"HasOneThrough"
出现下面,选择右边下面远程一对一的双击,跳转到文件源码"HasOneThrough.php"
也可以直接找这文件,这 HasOneThrough.php 文件的路径为
vendor\topthink\think-orm\src\model\relation\HasOneThrough.php
找到 eagerlyWhere 方法,大概140行,基本是最后那个方法
/**
* 关联模型预查询
* @access public
* @param array $where 关联预查询条件
* @param string $key 关联键名
* @param array $subRelation 子关联
* @param Closure $closure
* @param array $cache 关联缓存
* @return array
*/
protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array
{
// 预载入关联查询 支持嵌套预载入
$keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey);
if ($closure) {
$closure($this->getClosureType($closure));
}
$list = $this->query
->where($this->throughKey, 'in', $keys)
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
->select();
// 组装模型数据
$data = [];
$keys = array_flip($keys);
foreach ($list as $set) {
$data[$keys[$set->{$this->throughKey}]] = $set;
}
return $data;
}
相信细心的已经看到出问题的地方了,
正是这个array_flip()函数,这个函数的作用是:反转数组中的键名和对应关联的键值
举个简单的例子
原数组:Array ( [1] => 32 [2] => 32 )
array_flip()函数转换后
转换后: Array ( [32] => 2 )
这就是为什么相同的会部分显示为null 的原因了
解决方法
先贴修改后的代码
protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array
{
// 预载入关联查询 支持嵌套预载入
$keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey);
if ($closure) {
$closure($this->getClosureType($closure));
}
$list = $this->query
->where($this->throughKey, 'in', $keys)
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
->select();
// 组装模型数据
$data = [];
// start 原代码
// $keys = array_flip($keys);
// foreach ($list as $set) {
// $data[$keys[$set->{$this->throughKey}]] = $set;
// }
// end
// start 修改部分
$arr = [];
foreach ($keys as $k => $v) {
if (isset($arr[$v])) {
array_push($arr[$v], $k);
} else {
$arr[$v][] = $k;
}
}
foreach ($list as $set) {
if (is_array($arr[$set->{$this->throughKey}])) {
foreach ($arr[$set->{$this->throughKey}] as $key) {
$data[$key] = $set;
}
}
}
// end
return $data;
}
修改后: Array ( [32] => Array ( [0] => 1 [1] => 2 ) )
也就是简单的将一维数组,转化为二维数组,之后就可以正常使用了。
这个方法可能还有可以优化的地方,有的话评论告知,相互学习。