本文目录导读:

在PHP API开发中,“关联加载”(Eager Loading)通常指在一次查询中预加载关联数据,避免N+1查询问题,根据不同场景,控制方式如下:
Laravel Eloquent ORM(最常用)
使用 with() 预加载
// 基本用法 - 预加载关联
$users = User::with('posts')->get();
// 结果:每个用户都附带posts数据,只需2条SQL
// 多层关联
$users = User::with('posts.comments')->get();
// 条件约束
$users = User::with(['posts' => function ($query) {
$query->where('status', 'published')->orderBy('created_at', 'desc');
}])->get();
// 选择特定字段
$users = User::with('posts:id,user_id,title')->get();
// 多个关联
$users = User::with(['posts', 'profile', 'roles'])->get();
控制是否加载
// 条件加载
$showPosts = request()->has('include_posts');
$users = User::when($showPosts, function ($query) {
return $query->with('posts');
})->get();
延迟加载
// 先查询,需要时再加载关联
$users = User::all();
if (someCondition()) {
$users->load('posts'); // 手动加载关联
}
// 仅对未加载的关联加载
$users->loadMissing('posts');
ThinkPHP 框架
// 关联预加载
$list = User::with(['posts', 'profile'])->select();
// 条件预加载
$list = User::with(['posts' => function($query) {
$query->where('status', 1);
}])->select();
// 延迟预加载
$users = User::select();
$users->load(['posts', 'profile']);
原生PHP / 自定义实现
// 手动控制关联加载
class User {
public function loadPosts($userIds) {
// 批量查询关联数据
$posts = Post::whereIn('user_id', $userIds)->get()
->groupBy('user_id');
// 分配给对应用户
foreach ($this->users as $user) {
$user->setRelation('posts', $posts[$user->id] ?? []);
}
}
}
API参数控制(前端控制)
// 通过请求参数控制
public function index(Request $request)
{
$query = User::query();
// 允许前端指定要加载的关联
$allowedWith = ['posts', 'profile', 'roles'];
$with = array_intersect(
explode(',', $request->input('with', '')),
$allowedWith
);
if ($with) {
$query->with($with);
}
return $query->paginate();
}
性能优化最佳实践
避免过度加载
// ❌ 不好的做法
$users = User::with('posts', 'profile', 'roles', 'comments', 'likes')->get();
// ✅ 好的做法
$includes = array_intersect(
request('include', ''),
['posts', 'profile', 'roles']
);
$users->with($includes);
使用 loadCount 代替加载完整数据
// 如果只需要计数
$users = User::withCount('posts')->get();
// 每个用户有 $user->posts_count 属性
设置白名单
// 允许的关联列表
private $allowedWith = [
'posts' => '帖子',
'profile' => '个人信息',
'roles.permissions' => '角色与权限'
];
常见控制场景
分页时控制关联加载
// 仅在非分页时加载所有关联
if (!request()->has('page')) {
$query->with('posts');
}
根据资源类型决定
// 列表页只加载必要关联
if ($request->is('api/users')) {
$users->with('profile:id,avatar,nickname');
}
// 详情页加载完整数据
if ($request->is('api/users/*')) {
$users->with(['posts', 'profile', 'settings']);
}
总结建议
- 最小化加载:只加载客户端需要的数据
- 允许动态控制:通过API参数让前端决定关联
- 设置白名单:防止恶意请求加载过多数据
- 使用计数替代:不需要完整数据时用
withCount - 注意多层嵌套:深度嵌套会影响性能
选择哪种方式取决于你的框架和具体需求,Laravel 的 Eloquent 提供了最完善的关联加载控制机制。