YII2 关系数据表组装

前言

每个新手程序猿都会遇到一些奇奇怪怪的难题,比如说在两个或者多个数据关系表的情况下,需要以一个主表查询出对应关系表的数据,如下关系表说明

准备

数据表说明

  • 用户表(user)

《YII2 关系数据表组装》

  • 会员卡表(vip)

《YII2 关系数据表组装》

  • 会员卡附表(vip_fields)

《YII2 关系数据表组装》

使用场景

  • 可以使用在列表需要查询关联多表字段的接口需求

需求

  • 会员卡列表
  • 需要有会员昵称/头像/姓名/性别邮箱地址/会员id

实现

  • 根据(会员卡)找出会员用户信息以及会员卡附表数据进行关联,以下是两种示例

例1

  <?php
// 获取会员卡列表
 $vips = Vip::find()->offset(0)->limit(10)->asArray()->all();
 // 循环组装会员用户信息及会员卡附表数据
 foreach($vips as &$row){
  // 获取会员卡用户信息
     $row['user'] = User::find()->andWhre(['user_id'=>$row['user_id']])->limit(1)->asArray()->one();
  // 获取会员卡附表信息
     $row['fields'] = VipFIelds::find()->andWhere(['vip_id'=>'vip_id'])->limit(1)->asArray()->one();
 }  
 return $vips;
 ?>
  • 运行结果
[
    {
        "vip_id": 1,
        "user_id": 1,
        "user": {
            "user_id": 1,
            "nickname": "小白",
            "avatar": "http://xiaobai.png"
        },
        "fields": {
            "vip_id": 1,
            "realname": "小小白",
            "sex": 1,
            "email": "xiaobai@qq.com"
        }
    },
    {
        "vip_id": 2,
        "user_id": 2,
        "user": {
            "user_id": 2,
            "nickname": "小米",
            "avatar": "http://xiaobai.png"
        },
        "fields": {
            "vip_id": 2,
            "realname": "小小米",
            "sex": 1,
            "email": "xiaomi@qq.com"
        }
    }
]

例2

 <?php
// 会员卡列表
$vips = Vip::find()->offset(0)->limit(10)->asArray()->all();

// 用户id
$userIds = array_unique(array_column($vips,'user_id'));

// 会员卡id
$vipIds = array_unique(array_column($vips,'vip_id'));

 // 会员卡用户信息数据列表
$users = User::find()->andWhere(['user_id'=>$userIds])->asArray()->limit(count($userIds))->all();

// 重组会员卡信息数据 以user_id为key
$usersRow = array_column($users,null,'user_id');

// 会员卡附表数据列表
$fields = VipFields::find()->andWhere(['vip_id'=>$vipIds])->asArray()->limit(count($vipIds))->all();

// 重组会员卡附表数据 以 vip_id为key
$fieldsRow = array_column($fields,null,'vip_id');

foreach($vips as &$row){
        // 组装会员卡用户信息  不存在 为 null
        $row['user'] = $usersRow[$row['user_id']] ?? null;
        // 组装会员卡附表信息  不存在 为 null
        $row['fields'] = $fieldsRow[$row['vip']] ?? null;
}
return $vips;
?>
  • 运行结果
[
    {
        "vip_id": 1,
        "user_id": 1,
        "user": {
            "user_id": 1,
            "nickname": "小白",
            "avatar": "http://xiaobai.png"
        },
        "fields": {
            "vip_id": 1,
            "realname": "小小白",
            "sex": 1,
            "email": "xiaobai@qq.com"
        }
    },
    {
        "vip_id": 2,
        "user_id": 2,
        "user": {
            "user_id": 2,
            "nickname": "小米",
            "avatar": "http://xiaobai.png"
        },
        "fields": {
            "vip_id": 2,
            "realname": "小小米",
            "sex": 1,
            "email": "xiaomi@qq.com"
        }
    }
]

分析Yii特殊用法

Yii joinWith() 用法

  • 官方说明

    • 此方法将允许你重用现有的关联定义来执行 JOIN 查询。 基于指定关联的定义, 该方法将一个或多个 JOIN 语句附加到当前
    • 如果 $eagerLoading 参数为真,该方法还将对指定的关联执行即时加载, 相当于使用指定关联调用 with()。

      注意,因为将执行 JOIN 查询,所以你需要消除列名的歧义。

    • 此方法与 with() 不同之处在于,它将构建并执行主表的 JOIN SQL 语句。 并且当 $eagerLoading 为真时,除了指定关联之外,还将调用 with()。
  • 定义

    // 指定连接会员卡附表
    public function getFields()
    {
        return $this->hasOne(VipFields::class,['vip_id'=>'vip_id']);
    }
    
    // 指定连接会员卡用户表
    public function getUser()
    {
        return $this->hasOne(User::class,['user_id'=>'user_id']);
    }

注意以下几点:

1.当表名存在下划线时,joinWith里的表名首字母要小写,下划线结束后的第一个字母要大写

2.有where条件时,字段名前的表名,有些跨数据库的,数据库名要写全。

  • 使用

    • 如上面的需求场景使用
    <?php
    // 创建用户模型
    $query = Vip::find();
    // 使用模型joinWith连接
    $query->joinWith([
        'fields',
        'user'
    ]);
    $list = $query->offset(0)->limit(10)->asArray()->all();
    return $list;
    ?>

使用joinWith进行Vip模型连接时,同时使用field以及User模型连接的情况下,那么说明以vip做为主表,以vip_fields及user表做为附表,同时三表中必须存在关联数据才有查询结果


- 运行结果
[
    {
        "vip_id": 1,
        "user_id": 1,
        "user": {
            "user_id": 1,
            "nickname": "小白",
            "avatar": "http://xiaobai.png"
        },
        "fields": {
            "vip_id": 1,
            "realname": "小小白",
            "sex": 1,
            "email": "xiaobai@qq.com"
        }
    },
    {
        "vip_id": 2,
        "user_id": 2,
        "user": {
            "user_id": 2,
            "nickname": "小米",
            "avatar": "http://xiaobai.png"
        },
        "fields": {
            "vip_id": 2,
            "realname": "小小米",
            "sex": 1,
            "email": "xiaomi@qq.com"
        }
    }
]

后言

  • 例1 在foreach里面进行数据查询,数据库请求频繁,导致数据库压力较大,数据库请求次数 1+(N*2) 次,对数据库影响较大
  • 例2 在查询出会员卡主表后在将关联表关联id单独取出成为数组再进行一批列表查询,重组,数据库请求次数3次

总结

不同的需求都有不同的代码处理逻辑,以例1与例2为示例,虽然例1也能实现接口需求,但是对于数据库影响比较大,不推荐使用,例1虽说处理步骤较多,但是对数据影响较小,可以选择例2使用,如发现更好的方案会继续优化

    原文作者:小白
    原文地址: https://segmentfault.com/a/1190000019946833
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞