ThinkPHP 6模型关联实战:高效数据关系处理指南
一、模型关联基础
ThinkPHP提供了7种关联关系,满足不同业务场景:
// 用户模型定义关联
class User extends Model
{
// 一对多关联文章
public function articles()
{
return $this->hasMany(Article::class);
}
// 多对多关联角色
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
// 控制器中使用
$user = User::find(1);
// 获取用户所有文章
$articles = $user->articles;
// 获取用户所有角色
$roles = $user->roles;
二、关联查询优化
1. 延迟预加载
// 需要时再查询关联数据
$users = User::select();
foreach ($users as $user) {
// 这里才会实际查询文章数据
$articles = $user->articles;
}
// 预加载优化(N+1问题)
$users = User::with(['articles'])->select();
// 一次查询获取所有关联数据
2. 关联条件约束
// 只查询已发布的文章
$user->articles()
->where('status', 1)
->select();
// 关联定义时直接添加约束
public function publishedArticles()
{
return $this->hasMany(Article::class)
->where('status', 1);
}
三、高级关联技巧
1. 多态关联
// 评论可以属于文章或视频
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
// 文章模型
class Article extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
// 查询文章的所有评论
$article->comments;
2. 远程一对多
// 通过国家获取所有文章
class Country extends Model
{
public function articles()
{
return $this->hasManyThrough(
Article::class,
User::class,
'country_id', // 用户表外键
'user_id', // 文章表外键
'id', // 国家表主键
'id' // 用户表主键
);
}
}
// 使用示例
Country::find(1)->articles;
四、关联操作性能对比
查询方式 | 1000条数据耗时 | SQL查询次数 |
---|---|---|
延迟加载 | 1.2s | 1001 |
预加载 | 0.3s | 2 |
JOIN查询 | 0.4s | 1 |
五、实战案例:电商系统
1. 订单关联处理
class Order extends Model
{
// 关联用户
public function user()
{
return $this->belongsTo(User::class);
}
// 关联商品(中间表)
public function products()
{
return $this->belongsToMany(Product::class, 'order_product')
->withPivot('price', 'quantity');
}
// 关联收货地址
public function address()
{
return $this->hasOne(OrderAddress::class);
}
}
// 查询订单及所有关联
$order = Order::with(['user', 'products', 'address'])
->find($id);
2. 关联数据保存
// 创建订单及关联数据
$order = new Order([
'order_no' => generateOrderNo(),
'user_id' => $userId
]);
// 保存主表
$order->save();
// 关联商品(中间表)
$order->products()->saveAll([
[
'product_id' => 1,
'price' => 99,
'quantity' => 2
],
[
'product_id' => 2,
'price' => 199,
'quantity' => 1
]
]);
// 关联收货地址
$order->address()->save([
'consignee' => $data['name'],
'phone' => $data['phone'],
'address' => $data['address']
]);
六、最佳实践总结
- 关联命名使用复数形式表示一对多关系
- 频繁访问的关联务必使用预加载
- 多表关联考虑使用JOIN查询优化性能
- 关联操作注意事务处理保证数据一致性
- 复杂关联考虑使用模型观察器自动维护