laravel with()方法与load()方法

kfi*_*rba 41 laravel laravel-4

我真的试图理解with()方法和load()方法之间的区别,但无法理解.

我认为,使用该with()方法"更好",因为我急切地加载关系.似乎如果我使用load()我加载关系就好像我会使用hasMany()(或任何其他与对象之间的关系相关的方法).

我弄错了吗?

dam*_*ani 83

两者都实现了相同的最终结果 - 渴望将相关模型加载到第一个结果上.实际上,它们都运行完全相同的两个查询.关键的区别是,with()急于载荷相关的示范前面,立即初始查询后(all(),first(),或find(x),例如); 在使用时load(),首先运行初始查询,然后在稍后的某个时刻急切加载关系.

这里的"Eager"意味着我们只使用一个查询来关联特定结果集的所有相关模型,而不必运行n个查询,其中n是初始集合中的项目数.


渴望加载使用 with()

如果我们急于加载使用with(),例如:

$users = User::with('comments')->get(); 
Run Code Online (Sandbox Code Playgroud)

...如果我们有5个用户,则立即运行以下两个查询:

select * from `users`
select * from `comments` where `comments`.`user_id` in (1, 2, 3, 4, 5)
Run Code Online (Sandbox Code Playgroud)

...我们最终得到了一些模型,这些模型的注释附加在用户模型上,因此我们可以做类似的事情$users->comments->first()->body.


"懒惰"急切加载使用 load()

或者,我们可以将两个查询分开,首先获取初始结果:

$users = User::all();
Run Code Online (Sandbox Code Playgroud)

运行:

    select * from `users`
Run Code Online (Sandbox Code Playgroud)

后来,如果我们决定需要所有这些用户的相关评论,我们可以在事后加急加载:

$users = $users->load('comments');
Run Code Online (Sandbox Code Playgroud)

运行第二个查询:

    select * from `comments` where `comments`.`user_id` in (1, 2, 3, 4, 5)
Run Code Online (Sandbox Code Playgroud)

......我们最终得到了相同的结果,只分成两步.同样,我们可以调用$users->comments->first()->body以获取任何项目的相关模型.


为何使用load()vs. with()load()根据某些动态条件,您可以选择稍后决定是否需要运行第二个查询.但是,如果您无需访问所有相关项目,请使用with().(文档也引用了使用load()的缓存优势,但我对此并不熟悉;事实上,我认为结果load()不可缓存.)


其中任何一个的替代方案是循环遍历初始结果集并查询hasMany()每个项目的关系.这将最终运行n + 1个查询,或者在此示例中为6.无论是预先加载with()还是以后加载,都load()只需运行2次查询即可.

  • 这取决于你追求的数据.如果你只对相关的模型注释感兴趣,那么在我们的例子中 - 然后简单地查询`hasMany()`关系方法:`$ users = User :: find(1) - > comments;`,它只返回信息关于评论,而不是用户.但是,如果你还需要从初始模型访问数据 - 用户 - 那么`$ user = User :: where('id','1') - > with('comments') - > get(); `将为您提供一个包含来自User表*和*相关注释的数据的集合.无论哪种方式,都会运行2个相同的查询,但生成的集合是不同的. (3认同)
  • 谢谢!我还有一个问题,为什么我永远不会使用eager loading然后使用正常的`hasMany()`方法?(我认为我的答案的解决方案是,无论何时我只与一个用户打交道,无论我是否正在使用急切加载都没关系,因为无论如何它都会运行2个查询?) (2认同)

Rad*_*rma 13

正如 @damiani 所说,两者都实现了相同的最终结果\xe2\x80\x94eager 将相关模型加载到第一个模型上。事实上,它们都运行完全相同的两个查询。关键区别在于 with() 在初始查询之后立即预先加载相关模型(例如 all()、first() 或 find(x));使用 load() 时,您首先运行初始查询,然后在稍后的某个时刻预先加载关系。

\n\n

With() 和 load() 之间还有一个区别,使用 with() 时可以添加条件,但在 load() 中则不能这样做

\n\n

例如:

\n\n
ProductCategory::with('children')\n        ->with(['products' => function ($q) use($SpecificID) {\n            $q->whereHas('types', function($q) use($SpecificID) {\n                $q->where('types.id', $SpecificID)\n            });\n        }])\n        ->get(); \n
Run Code Online (Sandbox Code Playgroud)\n


Teo*_*gır 5

load()@damiani 也解释了和with()之间的区别,但他说load()不可缓存,所以我想对此说几句话。

假设我们有一篇博客文章并与评论相关。我们一起获取并缓存它。

$post = Cache::remember("post.".$slug,720,function()use($slug){
   return Post::whereSlug($slug)->with("comments")->first();
});
Run Code Online (Sandbox Code Playgroud)

但是,如果有新评论并且我们想立即显示它,我们必须清除帖子缓存并再次一起获取帖子和评论。这会导致不必要的查询。让我们假设还有对标签、媒体、帖子贡献者等的其他查询。这会增加资源使用量。

public function load($relations)
{
    $query = $this->newQueryWithoutRelationships()->with(
        is_string($relations) ? func_get_args() : $relations
    );

    $query->eagerLoadRelations([$this]);

    return $this;
}
Run Code Online (Sandbox Code Playgroud)

正如您在上面看到的,当我们使用该方法时,它会加载给定的关系并返回具有获取的关系的模型。所以你可以在回调之外返回它。

$post = Cache::remember("post.".$slug,720,function()use($slug){
   return Post::whereSlug($slug)->first();
});

$post = Cache::remember("post.relation.images.".$slug,720,function()use($post){
  return $post->load("images");
});

$post = Cache::remember("post.relation.comments".$slug,720,function()use($post){
   return $post->load("comments");
});
Run Code Online (Sandbox Code Playgroud)

因此,如果我们单独加载它们,下次当其中一些更新时,您需要清除特定关系缓存并再次获取它。无需一遍又一遍地获取帖子、标签、图像等。