【问题标题】:Laravel: What is the purpose of the `loadMissing` function?Laravel:`loadMissing` 函数的目的是什么?
【发布时间】:2018-04-24 15:16:27
【问题描述】:

Laravel 文档中Eager Loading section 的第一句话是:

当访问 Eloquent 关系作为属性时,关系 数据是“延迟加载”。这意味着关系数据不是 实际加载,直到您第一次访问该属性。

在本节的最后一段中指出:

要仅在尚未加载关系时加载关系,请使用 loadMissing 方法:

public function format(Book $book)
{
    $book->loadMissing('author');

    return [
        'name' => $book->name,
        'author' => $book->author->name
    ];
}

但我看不出$book->loadMissing('author') 的目的。它在这里做什么?

如果我只删除这一行会有什么不同?根据第一句话,$book->author->name 中的作者无论如何都会 lazy-loaded 对吧?

【问题讨论】:

    标签: php mysql laravel laravel-5


    【解决方案1】:

    很好的问题;有一些细微的差异不会通过阅读文档立即反映出来。

    您正在比较模型上使用loadMissing() 的“Lazy Eager Loading”和使用magic properties 的“延迟加载”。

    顾名思义,唯一的区别是:

    • “延迟加载”仅在使用关系时发生。
    • “急切延迟加载”可能在使用前发生。

    因此,实际上,除非您想在使用关系之前显式加载关系,否则没有区别。

    还值得注意的是,loadloadMissing 方法都让您有机会通过传递一个闭包来自定义关系加载逻辑,这在使用魔术属性时不是一个选项。

    $book->loadMissing(['author' => function (Builder $query) {
        $query->where('approved', true);
    }]);
    

    这意味着 “如果尚未加载,则加载缺少的已批准作者” 除非您在模型上定义 approvedAuthor 关系,否则这是无法使用 $book->author 实现的(尽管这是一种更好的做法)。


    直接回答您的问题;是的,如果你删除不会有任何区别:

    $book->loadMissing('author'); 
    

    在该特定示例中,因为它在加载后立即使用。但是,可能很少有使用情况下希望在使用关系之前加载关系。


    因此,概述关系加载方法的工作原理:

    渴望加载

    通过使用with(),您可以在查询父模型时“预先加载”关系:

    $book = Book::with('author')->find($id);
    

    延迟加载

    父模型已被检索到之后立即加载关系:

    $book->load('author');
    

    这也可能用于仅急切加载缺失的内容:

    $book->loadMissing('author');
    

    load() 方法相反,loadMissing() 方法过滤给定的关系并延迟“急切”地加载它们只有在尚未加载时

    通过接受闭包,这两种方法都支持自定义关系加载逻辑。

    延迟加载

    通过使用magic properties 发生的延迟加载是为了方便开发人员。它会在使用关系时加载关系,因此您无需事先加载它。


    @rzb 在his answer 中也提到了一个很好的观点。看看吧。

    【讨论】:

    • 谢谢,"Load missing approved author if not already loaded" 的好例子。只是为了确保我对您的理解正确:您的意思是 approvedAuthor 关系是这样的方法:return hasMany('App\Author')->where('approved', true); 吗?
    • 没错。关于更好的可读性以及避免重复的关系定义,我将其定义为:return $this->author()->where('approved', true);.
    • 我还发现了另一个用例。如果您必须访问像author.type 这样的子关系,那么如果没有loadMissing,您将为每个作者生成一个查询。
    • 我在这里遗漏或误解的一件事:为什么这些记录丢失了?它们是在关系条件中被跳过的记录,现在我们想忽略该条件吗?或者可能是在处理已经获取的内容时由另一个进程新插入的记录?
    • @sepehr 谢谢你的解释。但是我有一个问题:在这种情况下使用loadwith 可以避免N+1 问题,对吗?如果我们只是通过魔术属性使用延迟加载,可能会导致N+1,因为作者是在之后加载的。
    【解决方案2】:

    我认为接受的答案缺少一个可能误导某些人的重要事实:您不能在集合上运行 loadMissing($relation)

    这很重要,因为大多数惰性急切加载关系的用例是当您已经拥有一个集合并且您不想提交 n+1 罪时 - 即在循环中不必要地多次访问数据库。

    因此,虽然您可以在集合上使用 load($relation),但如果您只想在之前尚未加载关系的情况下这样做,那么您就不走运了。

    【讨论】:

    【解决方案3】:

    对 API 非常有用

    withloadMissingload 的使用在 API 环境中使用更重要,结果传递给json。在这种情况下,延迟加载没有任何作用。

    【讨论】:

      【解决方案4】:

      假设您有多个关系。

      书属于作者,书属于出版商。

      所以首先你可以用一个关系加载它。

      $books->load('author');
      

      然后在某些情况下,您想将另一个关系加载到其中。

      $book->loadMissing('publisher');
      

      但我看不到 $book->loadMissing('author'); 的用途。是吗 在这里做什么?如果我只是删除会有什么区别 这条线?根据第一句话,作者在 $book->author->name 无论如何都会被延迟加载,对吧?

      假设说

      public function format(Book $book)
      {
          //book will not have the author relationship yet  
      
          return [
              'name' => $book->name, //book will not have the author relationship loaded yet  
              'author' => $book->author->name //book will now have the author relationship 
          ];
      }
      

      上面和下面代码的区别在于何时加载关系以及您对属性的控制程度。

      public function format(Book $book)
      {
          $book->loadMissing('author'); // book will now have the author relationship
      
          return [
              'name' => $book->name, // book have the author relationship loaded
              'author' => $book->author->name // book have the author relationship loaded
          ];
      }
      

      【讨论】:

      • 但是既然它已经加载好了,你为什么要loadMissing?如果您想访问$book->publisher,无论有无loadMissing,都可以使用。
      • @Adam 看到我通过解释改进了答案。
      【解决方案5】:

      这里的两个答案都很好地涵盖了技术差异,所以我会先向您推荐他们。但“为什么”不是很明显。

      我发现自己最近经常宣扬的一点是,Eloquent 真的很擅长给你足够的绳子来吊死自己。通过将开发人员从生成的实际 SQL 查询中抽象出来,尤其是对于动态属性,当您的数据库命中对您的性能造成的影响超出其需要时,很容易忘记。

      事情就是这样。对 1000 个值使用 IN() 语句的一个查询与在 one 值上运行的一个查询所花费的执行时间大致相同。 SQL 非常擅长它的工作——性能下降通常伴随着打开和关闭数据库连接。这有点像去杂货店购物,需要为每件 件商品去一次市场,而不是一次性完成所有工作。 Eager-loads 使用 IN 语句。

      延迟加载适用于处理过多数据以使服务器的 RAM 无法处理的情况,而且在我看来,这对其他情况不利。它在任何给定时刻只处理一个条目。但它每次都重新连接。我无法告诉你我见过多少次 Transformer 类,它应该只负责重新格式化数据而不是检索它,利用这些动态属性并且没有意识到数据不存在。我已经看到改进,只需在调用 Transformer 之前添加一行急切加载,就可以将执行时间从 30 分钟显着减少到 30 秒。

      (顺便说一句,批处理可能被认为是快乐的媒介,Eloquent 的 chunk() 方法也提供了这一点。)

      更直接地回答你的问题;如果您正在处理一个一对一关系的实例,并且它只会在一个地方使用,那么在功能上loadloadMissing 或延迟加载动态之间没有区别财产。但是,如果您有一个多对多,那么一次收集所有这些数据可能是值得的。一本书可以有许多共同作者。一个作者可以写很多本书。如果您要循环浏览大量的任何一种,请在开始烹饪之前充分利用您的市场之旅。

      【讨论】:

        【解决方案6】:

        它的意思是不重复查询 说清楚 如果您使用:load() 2 次,即使存在关系,查询也会重复

        while : loadMissing() 是检查关系是否已加载。它不会重复查询。因为它之前已经通过 [ load() 或 with() ] = egear load 加载过

            DB::enableQueryLog();
        
            $user = User::find(1);
        
            // see the query 
        
            $user->load('posts');   
            $user->load('posts');  
            $user->loadMissing('posts'); // put it on top to see the difference 
        
            dd(DB::getQueryLog());
        

        这就是我认为它的目的

        【讨论】:

          猜你喜欢
          • 2018-01-18
          • 2017-02-10
          • 2012-01-14
          • 2013-05-22
          • 2012-06-08
          • 1970-01-01
          • 2021-06-09
          • 1970-01-01
          • 2014-01-19
          相关资源
          最近更新 更多