亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

Yii2多表關聯的用法示例

2020-03-22 17:57:30
字體:
來源:轉載
供稿:網友
  • 本篇博客是基于《活動記錄(Active Record)》中對于AR表關聯用法的介紹。

    我會構造一個業務場景,主要是測試我比較存疑的各種表關聯寫法,而非再次介紹基礎用法。

    構造場景 訂單ar_order order_id 訂單id(主鍵) user_id 用戶id
    用戶ar_user user_id 用戶id(主鍵) user_name 用戶名
    訂單商品清單ar_order_goods id 自增id(主鍵) order_id 所屬訂單id goods_id 所買商品id
    商品ar_goods goods_id 商品id(主鍵) goods_name 商品名稱

    商品庫存ar_stock

    stock_id 庫存id(主鍵) goods_id 商品id(唯一鍵) stock_count 庫存量

    表關系如下圖所示:

    machi-2016-12-12-14-09-08

    我們接下來的測試,均以'訂單'為主體,通過AR的ORM關聯來查詢出依賴的數據。

    環境準備

    除了建表,還需要用gii生成所有的AR類,另外日志至少需要開啟db相關的category才能在日志里看見執行的SQL是什么。

            'log' => [            'traceLevel' => YII_DEBUG ? 3 : 0,            'targets' => [                [                    'html' target='_blank'>class' => 'yiilogFileTarget',                    'levels' => ['info', 'error', 'warning', 'trace'],                    'categories' => ['yiidb*'],                ],            ],        ],
    簡單關聯 訂單與用戶 1:1

    數據:

    ar_order:ar_order

    ar_user:ar_user

    給ArOrder添加關聯:

        public function getUser() {        return $this->hasOne(ArUser::className(), ['user_id' => 'user_id']);    }

    測試lazyload:

        public function actionHasOne()    {        // 查訂單        $orders = ArOrder::find()->all();        foreach ($orders as $order) {            // 查訂單關聯的用戶            $user = $order->user;            // 打印用戶名            echo $user->user_name . PHP_EOL;        }    }
    lazyload sql:
    SELECT * FROM ar_orderSELECT * FROM `ar_user` WHERE `user_id`=1SELECT * FROM `ar_user` WHERE `user_id`=2

    測試eagerload:

        public function actionHasOne()    {        // 查訂單        $orders = ArOrder::find()->with('user')->all();        foreach ($orders as $order) {            // 查訂單關聯的用戶            $user = $order->user;            // 打印用戶名,輸出:owen            echo $user->user_name . PHP_EOL;        }    }

    eagerload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_user` WHERE `user_id` IN (1, 2)
    訂單與商品清單 1:n

    數據:

    ar_order_goods:ar_order_goods

    給ArOrder添加關聯:

        public function getOrderGoods() {        return $this->hasMany(ArOrderGoods::className(), ['order_id' => 'order_id']);    }

    lazyload測試:

        public function actionHasMany()    {        // 查訂單        $orders = ArOrder::find()->all();        foreach ($orders as $order) {            // 查訂單關聯的商品清單            $orderGoodsArr = $order->orderGoods;            // 打印每個商品ID            foreach ($orderGoodsArr as $orderGoods) {                echo $orderGoods->goods_id . PHP_EOL;            }        }    }

    lazyload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_order_goods` WHERE `order_id`=1SELECT * FROM `ar_order_goods` WHERE `order_id`=2

    eagerload測試:

        public function actionHasMany()    {        // 查訂單        $orders = ArOrder::find()->with('orderGoods')->all();        foreach ($orders as $order) {            // 查訂單關聯的商品清單            $orderGoodsArr = $order->orderGoods;            // 打印每個商品ID,輸出:1,2            foreach ($orderGoodsArr as $orderGoods) {                echo $orderGoods->goods_id . PHP_EOL;            }        }    }

    eagerload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_order_goods` WHERE `order_id` IN (1, 2)
    跨中間表關聯 訂單商品表商品清單表1:n關聯

    數據:

    ar_goods:ar_goods

    給ArOrder添加關聯:

        public function getOrderGoods() {        return $this->hasMany(ArOrderGoods::className(), ['order_id' => 'order_id']);    }    public function getGoods() {        return $this->hasMany(ArGoods::className(), ['goods_id' => 'goods_id'])->            via('orderGoods');    }

    注:getGoods中的第一個goods_id是指getOrderGoods關聯的ArOrderGoods中的goods_id,第二個goods_id是指ArGoods中的goods_id。

    lazyLoad測試:

        public function actionVia()    {        // 查訂單        $orders = ArOrder::find()->all();        foreach ($orders as $order) {            // 查訂單關聯的商品(跨中間表orderGoods)            $goodsArr = $order->goods;            // 中間表$order->orderGoods的數據在此也被拉回來            echo count($order->orderGoods) . PHP_EOL;            // 打印每個商品的名稱            foreach ($goodsArr as $goods) {                echo $goods->goods_name . ' ' . PHP_EOL;            }        }    }

    lazyload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_order_goods` WHERE `order_id`=1SELECT * FROM `ar_goods` WHERE `goods_id` IN (1, 2)SELECT * FROM `ar_order_goods` WHERE `order_id`=2SELECT * FROM `ar_goods` WHERE `goods_id` IN (1, 2)

    eagerload測試:

        public function actionVia()    {        // 查訂單        $orders = ArOrder::find()->with('goods')->all();        foreach ($orders as $order) {            // 查訂單關聯的商品(跨中間表orderGoods)            $goodsArr = $order->goods;            // 中間表$order->orderGoods的數據在此也被拉回來            echo count($order->orderGoods) . PHP_EOL;            // 打印每個商品的名稱            foreach ($goodsArr as $goods) {                echo $goods->goods_name . ' ' . PHP_EOL;            }        }    }

    eagerload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_order_goods` WHERE `order_id` IN (1, 2)SELECT * FROM `ar_goods` WHERE `goods_id` IN (1, 2)

    發現with僅指定goods關聯,則中間關聯orderGoods的查詢也被eager處理了。

    簡單關聯之級聯

    和跨中間表關聯實現的功能一致,但是不通過via實現,而是通過定義若干級聯的1:1或1:n關聯來加載數據。

    上述中間表關聯中,ArOrder是主體,orderGoods和goods都被注入在ArOrder對象身上,這樣的優點是eagerload可以優化整個查詢流程,減少db交互,同時冗余表達的goods對象少(只需要2個goods對象,由2個order共享,下面代碼可以測試):

    $orders[0]->goods[0] === $orders[1]->goods[0]

    另一種表達這種關系的方式是:arOrder->orderGoods->goods這種間接訪問的方式,這樣僅需要維護arOrder和orderGoods間的1:n關系以及orderGoods和Goods間的1:1關系既可,優點是訪問方式更能體現表關聯的間接性,但是缺點就是eagerload無法完整優化整個流程,同時goods對象冗余多。

    訂單,商品表 ,商品清單表級聯

    ArOrderGoods添加關聯:

        public function getGoods() {        return $this->hasOne(ArGoods::className(), ['goods_id' => 'goods_id']);    }

    lazyload測試:

        public function actionNoVia()    {        $orders = ArOrder::find()->all();        foreach ($orders as $order) {            $orderGoodsArr = $order->orderGoods;            foreach ($orderGoodsArr as $orderGoods) {                $goods = $orderGoods->goods;                echo $goods->goods_name . PHP_EOL;            }        }    }

    lazyload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_order_goods` WHERE `order_id`=1SELECT * FROM `ar_goods` WHERE `goods_id`=1SELECT * FROM `ar_goods` WHERE `goods_id`=2SELECT * FROM `ar_order_goods` WHERE `order_id`=2SELECT * FROM `ar_goods` WHERE `goods_id`=1SELECT * FROM `ar_goods` WHERE `goods_id`=2

    eagerload測試:

        public function actionNoVia()    {        // 第一級關系eagerload        $orders = ArOrder::find()->with('orderGoods')->all();        foreach ($orders as $order) {            // 第二級關系eagerload            $orderGoodsArr = $order->getOrderGoods()->with('goods')->all();            foreach ($orderGoodsArr as $orderGoods) {                $goods = $orderGoods->goods;                echo $goods->goods_name . PHP_EOL;            }        }    }

    eagerload sql:

    SELECT * FROM `ar_order`SELECT * FROM `ar_order_goods` WHERE `order_id` IN (1, 2)SELECT * FROM `ar_order_goods` WHERE `order_id`=1SELECT * FROM `ar_goods` WHERE `goods_id` IN (1, 2)SELECT * FROM `ar_order_goods` WHERE `order_id`=2SELECT * FROM `ar_goods` WHERE `goods_id` IN (1, 2)

    可見,級聯方式的交互總是比中間表方式要多,內存占用也要多,雖然經過eagerload優化可以減少幾次交互。

    joinWith 多表關聯

    Yii2支持數據庫的join語法,不過在編程的時候不是a表join b表這樣的表達方式,而是a表通過哪個關聯進行join,這個關聯就是我們之前定義的hasOne和hasMany,它們是不需要變動的。

    不過Yii2的JOIN并不是你想的那樣:'一句SQL查回所有的關聯數據,填充到關聯關系里',這是非常特殊的地方,文檔里這樣提到:

    joinWith()和with()的差別在于前者是聯合查詢,即通過把查詢條件應用于主表和關聯表來獲取主表記錄,而后者是關聯查詢,即只是針對主表查詢條件獲取主表記錄。

    因為這個差別,你可以應用JOIN SQL語句特有的查詢條件。比如你可以通過限定關聯表的條件來過濾主表記錄,如上述例子所示。你還可以通過關聯表列值來對主表記錄進行排序。

    說白了,joinWith雖然是使用數據庫的join語法實現的多表聯查,但是它不會一次性的將依賴表的數據保存起來,與with相比,僅僅是額外提供了一個根據依賴表的數據過濾主表數據的機會,依賴表的數據依舊會通過再次交互的方式進行查詢,是不是既失望又好奇呢?

    訂單,商品清單,商品 JOIN

    測試:

        public function actionJoin() {        $orders = ArOrder::find()->innerJoinWith([            'user' => function($query) {                $query->onCondition([                    '!=', 'user_name', 'john'                ]);            },            'goods' => function ($query) {                $query->onCondition([                    'and',                    [                        '!=', 'goods_name', '雪碧'                    ],                ]);            }        ])->all();        foreach ($orders as $order) {            $goodsArr = $order->goods;            foreach ($goodsArr as $goods) {                echo $goods->goods_name . PHP_EOL;            }        }    }

    sql:

    SELECT `ar_order`.* FROM `ar_order` INNER JOIN `ar_user` ON (`ar_order`.`user_id` = `ar_user`.`user_id`) AND (`user_name` != 'john') INNER JOIN `ar_order_goods` ON `ar_order`.`order_id` = `ar_order_goods`.`order_id` INNER JOIN `ar_goods` ON (`ar_order_goods`.`goods_id` = `ar_goods`.`goods_id`) AND ((`goods_name` != '雪碧'))SELECT * FROM `ar_user` WHERE (`user_id`=1) AND (`user_name` != 'john')SELECT * FROM `ar_order_goods` WHERE `order_id`=1SELECT * FROM `ar_goods` WHERE (`goods_id` IN (1, 2)) AND ((`goods_name` != '雪碧'))

    分析:

    你會發現,joinWith的確不是我們所想的一次SQL交互拉回所有依賴數據,而是用于縮小主體數據的規模,這也是為什么后續拉取依賴的時候,需要將依賴表的過濾條件再次套用的原因。

    通過最后的例子,我們可以明顯的感受出:ORM背后的行為并不一定是我們預期的那樣!

    所以,當我們使用ORM進行表關聯的時候,需要認真考慮一下是不是裸寫SQL的方式性能更佳,但是也別忘記ORM給我們帶來的抽象性和編程效率。

    感興趣請點擊關注我,歡迎討論。

    PHP編程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲图片制服诱惑| 精品色蜜蜜精品视频在线观看| 日韩国产欧美精品在线| 97精品在线视频| 亚洲影院高清在线| 亚洲人成电影网站色xx| 97国产精品免费视频| 国产精品久久久久久影视| 欧美久久精品午夜青青大伊人| 欧美午夜精品伦理| 欧美激情亚洲综合一区| 国产精品免费久久久久影院| 亚洲免费av网址| 色午夜这里只有精品| 中文字幕精品av| 91网站免费观看| 亚洲国产精品成人va在线观看| 欧美最猛性xxxxx亚洲精品| 亚洲国产精品va在线看黑人动漫| 国产精品偷伦一区二区| 国内精品久久久久伊人av| 中文国产成人精品| 中文在线不卡视频| 国产精品白嫩美女在线观看| 久久亚洲影音av资源网| 青青在线视频一区二区三区| 久久久精品久久久久| 国产精品视频一区二区高潮| 国产一区二区三区中文| 日本高清视频一区| 国产视频丨精品|在线观看| 国产一区二区三区日韩欧美| 久久精品久久精品亚洲人| 久久久亚洲精品视频| 欧美一级电影免费在线观看| 日本三级韩国三级久久| 欧美大尺度在线观看| 国产精品久久久久久久app| 91色p视频在线| 秋霞av国产精品一区| 2019中文字幕免费视频| 亚洲天堂视频在线观看| 亚洲免费av片| 性色av一区二区三区| 日韩福利在线播放| 精品久久中文字幕久久av| 精品免费在线观看| 日韩精品在线免费播放| 亚洲精品一区二区在线| 精品亚洲国产视频| 国产精品一香蕉国产线看观看| 91精品国产高清久久久久久91| 97在线观看视频| 精品国产福利在线| 在线观看欧美成人| 岛国av午夜精品| 亚洲国产精品久久久久| 中日韩美女免费视频网址在线观看| 亚洲国产精品小视频| 日韩在线欧美在线| 蜜臀久久99精品久久久无需会员| 欧美色视频日本版| 欧美成年人视频网站| 91夜夜揉人人捏人人添红杏| 欧美精品在线极品| 欧美与欧洲交xxxx免费观看| 自拍偷拍亚洲精品| 国外成人在线直播| 少妇高潮久久77777| 欧美精品18videos性欧| 精品亚洲国产成av人片传媒| 欧美国产极速在线| 日本a级片电影一区二区| 日韩电影中文字幕av| 国内精品国产三级国产在线专| 91九色单男在线观看| 国产精品国产三级国产aⅴ9色| 亚洲欧美三级伦理| 57pao精品| 91人人爽人人爽人人精88v| 欧美在线中文字幕| 国产不卡av在线免费观看| 国产精品精品视频一区二区三区| 精品久久久久久| 欧美俄罗斯乱妇| 亚洲第一男人av| 色综合视频网站| 97超视频免费观看| 久久久久久久久久久久av| 欧美国产日韩一区二区三区| 日韩国产欧美区| 欧美精品videossex性护士| 国产精品电影网站| 国产成人短视频| 97涩涩爰在线观看亚洲| 91精品国产91久久久久久久久| 国产成人精品免费视频| 国产精品三级久久久久久电影| 国产日韩欧美在线看| 欧美精品激情在线观看| 欧美精品激情blacked18| 久久精视频免费在线久久完整在线看| 久久伊人91精品综合网站| 国产精品九九久久久久久久| 日韩美女激情视频| 欧美日韩国产综合新一区| 国产成人精品国内自产拍免费看| 日韩女在线观看| 欧美老女人在线视频| 成人观看高清在线观看免费| 欧美日韩第一页| 亚洲天堂开心观看| 欧美日韩在线观看视频小说| 国产一区二区欧美日韩| 国产精品白嫩初高中害羞小美女| 亚洲精品aⅴ中文字幕乱码| 国产精品久久视频| 成人精品网站在线观看| 亚洲一区第一页| 欧美日韩在线视频一区| 亚洲成人av片在线观看| 国产精品国产自产拍高清av水多| 国内精品400部情侣激情| 精品美女久久久久久免费| 亚洲第一天堂av| 国产精品福利网| 亚洲精品一区二三区不卡| 久久久精品影院| 亚洲综合精品伊人久久| 丁香五六月婷婷久久激情| 国产成一区二区| 久久视频在线视频| 国产精品福利久久久| 麻豆国产精品va在线观看不卡| 成人黄色av免费在线观看| 亚洲黄页视频免费观看| 久久久久久国产| 全亚洲最色的网站在线观看| 国产一区二区三区直播精品电影| 欧美日韩国产在线看| 国产精品一区二区三区久久| 欧美日韩一区二区在线| 日韩精品在线私人| 懂色av一区二区三区| 亚州欧美日韩中文视频| 日韩在线视频网| 亚洲成人999| 555www成人网| 亚洲欧美成人在线| 久久精品国产一区二区电影| 久久久久久免费精品| 国产精品久久久久久影视| 午夜精品视频在线| 亚洲色图17p| 亚洲国产精彩中文乱码av| 久久久久久亚洲精品中文字幕| 欧美在线日韩在线| 国产日韩在线亚洲字幕中文| 日本午夜在线亚洲.国产| 日韩av手机在线看| 欧美激情图片区| 国产日本欧美视频| 欧美最猛性xxxxx免费| 亚洲国产成人一区|