YII2 關係數據表組裝

前言

每一個新手程序猿都會遇到一些奇奇怪怪的難題,好比說在兩個或者多個數據關係表的狀況下,須要以一個主表查詢出對應關係表的數據,以下關係表說明php

準備

數據表說明

  • 用戶表(user)

圖片描述

  • 會員卡表(vip)

clipboard.png

  • 會員卡附表(vip_fields)

clipboard.png

使用場景

  • 可使用在列表須要查詢關聯多表字段的接口需求

需求

  • 會員卡列表
  • 須要有會員暱稱/頭像/姓名/性別郵箱地址/會員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 查詢,因此你須要消除列名的歧義。api

    • 此方法與 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']);
    }

注意如下幾點:yii

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表作爲附表,同時三表中必須存在關聯數據纔有查詢結果this


- 運行結果
[
    {
        "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使用,如發現更好的方案會繼續優化spa

相關文章
相關標籤/搜索