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

Posted

技术标签:

【中文标题】laravel with() 方法与 load() 方法【英文标题】:laravel with() method versus load() method 【发布时间】:2014-11-18 07:16:17 【问题描述】:

我真的很想了解with()方法和load()方法之间的区别,但无法真正理解。

在我看来,使用with() 方法“更好”,因为我渴望加载关系。似乎如果我使用load() 我加载关系就像我会使用hasMany() (或与对象之间的关系相关的任何其他方法)。

我理解错了吗?

【问题讨论】:

【参考方案1】:

两者都完成了相同的最终结果——渴望将相关模型加载到第一个模型上。事实上,它们都运行完全相同的两个查询。关键区别在于with() 在初始查询之后立即预先加载相关模型(例如all()first()find(x));当使用load() 时,首先运行初始查询,然后在稍后的某个时间点加载关系。

这里的“渴望”意味着我们为特定结果集关联所有相关模型只使用一个查询,而不是必须运行 n 个查询,其中 n 是初始集合中的项目数。


使用with()进行预加载

如果我们使用with()进行预加载,例如:

$users = User::with('comments')->get(); 

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

select * from `users`
select * from `comments` where `comments`.`user_id` in (1, 2, 3, 4, 5)

...我们最终得到了一个将 cmets 附加到用户模型的模型集合,因此我们可以执行$users->comments->first()->body 之类的操作。


使用load()的“延迟”急切加载

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

$users = User::all();

运行:

select * from `users`

然后,如果我们决定需要所有这些用户的相关 cmets,我们可以在事后立即加载它们:

$users = $users->load('comments');

运行第二个查询:

select * from `comments` where `comments`.`user_id` in (1, 2, 3, 4, 5)

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


为什么使用load()with()load() 让您可以选择稍后根据某些动态条件决定是否需要运行第二个查询。但是,如果毫无疑问您需要访问所有相关项目,请使用with()


其中任何一个的替代方法是遍历初始结果集并查询每个项目的hasMany() 关系。这最终会运行 n+1 个查询,在本例中为 6。急切加载,无论是预先使用 with() 还是稍后使用 load() 完成,都只运行 2 个查询。

【讨论】:

谢谢!我还有一个问题,为什么我不使用急切加载并使用普通的hasMany() 方法? (我认为我的答案的一个解决方案是,每当我只与一个用户打交道时,我是否使用急切加载并不重要,因为它无论如何都会运行 2 个查询?) 这取决于您所追求的数据。如果您只对相关模型(在我们的示例中为 cmets)感兴趣,那么只需查询 hasMany() 关系方法:$users = User::find(1)->comments;,它将仅返回有关 cmets 的信息,而不是用户的信息。但是,如果您还需要访问来自初始模型(用户)的数据,那么 $user = User::where('id','1')->with('comments')->get(); 将为您提供一个集合,其中包含来自 User 表的数据他们的相关 Comments。无论哪种方式,都会运行 2 个相同的查询,但结果集合不同。 load 可以像with 一样嵌套吗?喜欢这里 - ***.com/questions/47048804/… withitem() 有什么作用?【参考方案2】:

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

假设我们有一篇与 cmets 相关的博客文章。我们正在一起获取并缓存它。

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

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

public function load($relations)

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

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

    return $this;

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

$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");
);

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

【讨论】:

【参考方案3】:

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

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

例如:

ProductCategory::with('children')
        ->with(['products' => function ($q) use($SpecificID) 
            $q->whereHas('types', function($q) use($SpecificID) 
                $q->where('types.id', $SpecificID)
            );
        ])
        ->get(); 

【讨论】:

以上是关于laravel with() 方法与 load() 方法的主要内容,如果未能解决你的问题,请参考以下文章

laravel 渴求式加载 with与load

CSRF 令牌与 Laravel API 不匹配,使用 Digital Ocean Load Balancer with Sticky Session

$query->with() 正在工作,但 $query->load() 在 Laravel 中不起作用

Laravel 查询&数据库&模型

Laravel Eloquent Where and or Where with Eager Load 无法正常工作

Laravel 的 with 方法返回 null