【Thinkphp6】tp6 hasOneThrough 远程一对一关联查询 数据查询出现null的异常问题分析

问题描述

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;
    }

相信细心的已经看到出问题的地方了,
bug处
正是这个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 ) )
也就是简单的将一维数组,转化为二维数组,之后就可以正常使用了。
这个方法可能还有可以优化的地方,有的话评论告知,相互学习。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThinkPHP6中,实现一对多关联查询的方法是使用hasMany模式。具体设置方式如下: 1. 在主表的模型中使用hasMany方法进行关联设置: ``` public function 附表名() { return $this->hasMany(附表模型::class, '外键字段', '主键字段'); } ``` 例如: ``` public function profile() { return $this->hasMany(Profile::class, 'user_id', 'id'); } ``` 2. 进行一对多关联查询: ``` $user = UserModel::find(主表记录的id); return json($user->附表关联方法名); ``` 例如: ``` $user = UserModel::find(19); return json($user->profile); ``` 3. 进行数据筛选查询: ``` $user->附表关联方法名->where('条件字段', '条件操作符', '条件值'); ``` 例如: ``` $user->profile->where('id', '>=', 10); ``` 4. 使用has()方法查询满足条件的主表记录: ``` UserModel::has('附表关联方法名', '条件操作符', '条件值')->select(); ``` 例如: ``` UserModel::has('profile', '>=', 2)->select(); ``` 5. 使用together()方法,在删除主表记录时,同时删除关联的附表记录: ``` $user = UserModel::with('附表关联方法名')->find(主表记录的id); $user->together(['附表关联方法名'])->delete(); ``` 例如: ``` $user = UserModel::with('profile')->find(22); $user->together(['profile'])->delete(); ``` 以上就是在ThinkPHP6中实现一对多关联查询的方法。希望对你有所帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值