thinkphp-关联预载入、关联统计
关联预载入
先来看这么一个查询
$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'
]));