thinkphp-关联预载入、关联统计

4354次阅读 53人点赞 作者: WuBin 发布时间: 2024-05-10 10:55:50
扫码到手机查看

关联预载入

先来看这么一个查询

 $list = UserModel::select([19, 20,21]);
foreach($list as $user) {
            // 每次执行关联查询的时候 又会调用sql语句
            dump($user->profile->toArray());
}

当我们在控制器中执行上面代码时,会发现执行了4次查询。如下:

SELECT * FROM `tp_user` WHERE `id` IN (19,20,21) [ RunTime:0.000842s ]
SELECT * FROM `tp_profile` WHERE `user_id` = 19 [ RunTime:0.000631s ]
SELECT * FROM `tp_profile` WHERE `user_id` = 20 [ RunTime:0.000309s ]
SELECT * FROM `tp_profile` WHERE `user_id` = 21 [ RunTime:0.000328s ]

所以当依次查询100条数据时候,就会执行101次;

在普通的关联查询下,我们循环数据列表会执行n+1 次SQL 查询;

避免损耗,于是做如下修改:

 $list = UserModel::with(['profile'])->select([19, 20,21]);
        foreach($list as $user) {
            dump($user->profile->toArray());
        }

此时的SQL语句:

SELECT * FROM `tp_user` WHERE `id` IN (19,20,21) [ RunTime:0.001007s ]
SELECT * FROM `tp_profile` WHERE `user_id` IN (19,20,21) [ RunTime:0.000936s ]

这时,哪怕查询100条数据,也是执行2次。

这里只记忆::with(['profile']) 数组支持多个值,即可以同时查询多个附表
关联预载入减少了查询次数提高了性能,但是不支持多次调用;

如果你有主表关联了多个附表,都想要进行预载入,可以传入多个模型方法即可

无论是一对一还是一对多,实际都是一张表对应另一张表的多条数据

比如目前主表user,关联两张附表是book和profile

app/model

// Profile.php
namespace app\model;
use think\Model;

class Profile extends Model {
  // 空即可 
}
// Book.php
namespace app\model;
use think\Model;

class Book extends Model {

}
// User.php

namespace app\model;
use think\Model;

class User extends Model {

    // 一对多
    public function profile()
    {
        return $this->hasMany(Profile::class, 'user_id');
    }

    public function book(){
        return $this->hasMany(Book::class, 'user_id');
    }

}

在控制器中

namespace app\controller;
use app\model\User as UserModel;

class Grade {
     public function load() {
         $list = UserModel::with(['book', 'profile'])->select([19, 20,21]);
        foreach($list as $user) {
            dump($user->profile . $user->book);
        }
    }

}

只需要传入两个表的模型类即可。

如果想要在关联模型实现链式操作,可以使用闭包,比如添加->field();

$list = UserModel::with(['profile'])->field('id, username')->select([19, 20,21]);
foreach($list as $user) {
            dump($user->profile->toArray());
}

此时的SQL语句:

SELECT `id`,`username` FROM `tp_user` WHERE `id` IN (19,20,21) [ RunTime:0.000990s ]
SELECT * FROM `tp_profile` WHERE `user_id` IN (19,20,21) [ RunTime:0.000843s ]
 $list = UserModel::with(['profile' => function($query) {
            // user_id 是外键是必须写的
            $query->field('user_id, hobby');
         }])->field('id, username')->select([19, 20,21]);
        foreach($list as $user) {
            dump($user->profile->toArray());
        }

其中id和user_id是必须要写的。

关联预载入还提供了一个延迟预载入,就是先执行select()再load()载入

意思就是拿到数据之后,可以先操作一波,然后再载入(效果与上面一样)。

// 先得到数据集
$list = UserModel::select([19, 20,21]);
 // 对list进行一系列操作后 再载入
$list->load(['profile']);
// 后面foreach这里就跟上面一样了
foreach($list as $user) {
        dump($user->profile->toArray());
 }

关联统计和输出

关联统计

统计一下主表中的记录,在附表中有多少关联的记录。

在控制器中

// withCount(['关联的表名'])
        $list = UserModel::withCount(['profile'])->select([
            19,20,21
        ]);
        foreach($list as $user){
            echo $user->profile_count;
            echo '<br>';
        }

关联统计的输出采用“关联方法名”_ count,这种结构输出

不单单支持Count,还有如下统计方法,均可支持,withMax()、withMin()、withSum()、withAvg()等;

除了withCount()不需要指定字段,其它均需要指定统计字段;

分别计算主表id=19 20 在附表中找到user_id=19的所有行中status属性的和。

$list = UserModel::withSum(['profile'], 'status')->select([
    19,20,21
]);
foreach($list as $user){
    echo $user->profile_sum;
    echo '<br>';
}

对于输出的属性,可以自定义

$list = UserModel::withSum(['profile' => 'ps'], 'status')->select([
    19,20,21
]);
foreach($list as $user){
    echo $user->ps;
    echo '<br>';
}

关联输出

就是之前数据集的操作。

使用hidden()方法,隐藏主表字段或附属表的字段

$list = UserModel::with(['profile'])->select([
        19,20,21
    ]);
return json($list->hidden([
    // 隐藏pad gender 
    'password', 'gender'
]));

输出

[
{
id: 19,
username: "蜡笔小新",
email: "xiaoxin@163.com",
create_time: "2016-06-27 16:45:26",
update_time: "1997-01-01 01:01:01",
profile: [
{
id: 1,
user_id: 19,
hobby: "酷爱科比",
status: 1
},
{
id: 37,
user_id: 19,
hobby: "不喜欢吃青椒",
status: 1
},
...
]
},
...
]

如果要隐藏profile:

return json($list->hidden([
    // 隐藏pad gender 
    'password', 'gender', 'profile'
]));

如果要隐藏profile中的某个属性

return json($list->hidden([
    // 隐藏pad gender 
    'password', 'gender', 'profile.status'
]));

如果是只显示

return json($list->visible([
    // 隐藏pad gender 
    'password', 'gender', 'profile.status'
]));

使用append()方法,添加一个额外字段,比如另一个关联的对象模型;

$list = UserModel::with(['profile', 'book'])->select([
        19,20,21
    ]);
return json($list->hidden([
    'password', 'gender', 'profile.status'
]));

输出

[
    {
        "id":19,
        "username":"蜡笔小新",
        "email":"xiaoxin@163.com",
        "profile":[
            {
                "id":1,
                "user_id":19,
                "hobby":"酷爱科比"
            }
        ...
        ],
        "book":[
            {
                "id":1,
                "user_id":19,
                "title":"《莎士比亚》"
            }
        ]
    },
    ...
]

或者使用append(),效果与上面的相同

$list = UserModel::with(['profile'])->select([
        19,20,21
    ]);
$list->append(['book']);
return json($list->hidden([
    'password', 'gender', 'profile.status'
]));
点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:thinkphp
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    8337次阅读 531人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    3474次阅读 255人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    13422次阅读 867人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    12217次阅读 392人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    10562次阅读 846人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    14437次阅读 1153人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    14342次阅读 1167人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    27483次阅读 2157人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    23619次阅读 1820人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录