在 Laravel 中使用 Auto Eager Loading 全局防止 N+1 问题
Laravel 中的 Eager Loading 是一种在查询模型时加载相关模型的方法。这样做是为了防止 N+1 问题,当加载一个模型,并在随后逐一加载其关联模型时,会导致对数据库的多个查询。
这可能是一个性能问题,尤其是当你有很多相关的模型时。快速加载(Eager Loading)允许你在单个查询中加载所有相关模型,这可以显著提高性能。
例如,如果你有一个包含许多帖子(Post)模型的用户(User)模型,你可以像这样快速加载帖子:
$users = User::with('posts')->get();
foreach ($users as $user) {
// This will not run a separate query for each user
echo $user->posts->title;
}
这将会在单个查询中加载所有帖子,而不会像如下这样一个个逐一加载:
$users = User::all();
foreach ($users as $user) {
// This will run a separate query for each user
echo $user->posts->title;
}
这将会导致 N+1 问题,其中有一个查询来加载所有的用户,然后每个用户一个查询来加载他们的帖子。
如你所见,Laravel 中 Eager Loading 并不是默认行为。你必须使用 with()
方法显式告知 Laravel 快速加载关联模型。
要修复这个问题,Laravel 现在有一个方式全局启用 Eager Loading 使得可以快速自动加载模型。
全局自动 Eager Loading
Laravel 12.x 在 Model
中引入一个新的方法叫做 automaticallyEagerLoadRelationships()
。该方法允许你启用在应用中跨越所有模型启用自动 Eager Loading。
重要:此功能仍处于测试阶段,Laravel 团队正在收集社区对此的反馈。因此,将来可能会此功能进行一些更改。
将下列代码添加到 AppServiceProvider
可以启用该功能:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Model::automaticallyEagerLoadRelationships();
}
}
因此,现在,正如我之前所展示的,即使是有 N+1 问题的有问题的代码,也可以正常工作。
// Since auto eager loading is enabled globally,
// this will work without any N+1 issues
$users = User::all();
foreach ($users as $user) {
echo $user->posts->title;
}
模型自动 Eager Loading
如果你不想全局启用自动热加载,你也可以在每个模型基础上启用它。你可以通过在模型中添加 automaticallyEagerLoadRelationships()
方法来实现这一点。
use App\Models\User;
User::automaticallyEagerLoadRelationships();
在查询上自动 Eager Loading
你还可以在每个查询的基础上启用自动渴望加载。可以通过使用 withRelationshipAutoloading()
方法来实现这一点,如下所示:
$users = User::all()->withRelationshipAutoloading();
foreach ($users as $user) {
echo $user->posts->title;
}
结论
我认为这是 Laravel 中一个很好的功能,对于不熟悉渴望加载的新手来说,这可能是一个救星。