【问题标题】:Laravel 5: Lazy Eager Loading without repeating queriesLaravel 5:无需重复查询的延迟加载
【发布时间】:2016-09-25 17:17:32
【问题描述】:

在我的项目中,我正在我的一个模型上编写一个方法,该模型使用其中一个关系和一个子关系,因此我不得不使用延迟加载:

class MyModel extends Model
{
    public function doSomething()
    {
        // Lazy eager load relationship and subrelationship
        $this->load('relationship', 'relationship.subrelationship');

        $relatedItems = $this->relationship;
        foreach ($relatedItems as $relatedItem) {
            $subrelatedItems = $relatedItem->subrelationship;
            foreach ($subrelatedItems as $subrelatedItem) {
                // Do something...
                return true;
            }
        }

        return false;
    }
}

Laravel 中的Model::load() 方法可用于重新加载关系并每次执行新的数据库查询。因此,每次我调用我的方法MyModel::doSomething(),(或调用另一个使用相似关系的方法)都会执行另一个数据库查询。

我知道在 Laravel 中你可以像这样多次调用关系:

$relatedItems = $model->relationship;
$relatedItems = $model->relationship;
$relatedItems = $model->relationship;
$relatedItems = $model->relationship;

..它不会重复查询,因为它已经加载了关系。

我想知道每次我想在模型中使用我的关系时是否可以避免查询数据库?我的想法是我可以使用$this->getRelations() 来确定哪些关系已经加载,然后如果它们已经加载就跳过它们:

$toLoad = ['relationship', 'relationship.subrelationship'];
$relations = $this->getRelations();
foreach ($toLoad as $relationship) {
    if (array_key_exists($relationship, $relations)) {
        unset($toLoad[$relationship]);
    }
}

if (count($toLoad) > 0) {
    $this->load($toLoad);
}

这在一定程度上是可行的,它每次都可以跳过加载relationship,但relationship.subrelationship实际上并没有存储在$this->getRelations()返回的数组中。我想它以subrelationship 的形式存储在子模型中。

干杯

【问题讨论】:

    标签: php laravel orm laravel-5 eloquent


    【解决方案1】:

    我认为这里的问题是,您正在模型文件中创建函数。这种限制你,你必须每次都加载关系。

    我要做的是创建一个函数,它将你想要做的模型对象作为参数。让我澄清一下:

    定义一个函数

    现在,如果您遵循设计模式,您可能有一个放置函数的位置。否则,将它放在您通常放置的位置。

    public function doSomething($myModels)
    {
        $relatedItems = $myModels->relationship;
        foreach ($relatedItems as $relatedItem) {
            $subrelatedItems = $relatedItem->subrelationship;
            foreach ($subrelatedItems as $subrelatedItem) {
                // Do something...
                return true;
            }
        }
    
        return false;
    }
    

    现在调用函数时,传递具有关系的模型。

    例如

    $myModels = MyModel::where('created_at', <, Carbon::now())->with('relationship')->get();
    doSomething($myModels)
    

    如果您需要加载多个或更深的关系,您可以这样做

    ...->with('relationship.subrelationship', 'secondrelationship')->get()
    

    代码未经测试,但我想你明白了。查看with()

    【讨论】:

    • 感谢您的回答 - 我明白您的意思。就我而言,我认为该方法适合该模型。给定一个用户对象(通过Auth::user(),我试图找出有关该用户的一些信息),因此使用with 非常困难,因为模型已经加载。我正在尝试实现类似于 with 所做的事情,因为我想加载我的关系,然后正常使用它们而不会遇到 n+1 问题。非常感谢您的回答 - 谢谢!
    • 您如何检索模型?在调用-&gt;get()-&gt;first() 之前,您可以调用-&gt;with(),它也已加载。
    • 我使用Auth::user() 检索模型,然后调用$this-&gt;relationship 之类的关系,即魔术方法。我已经尝试过$this-&gt;relationship()-&gt;with('subrelationship')-&gt;get(),但这仍然意味着重复查询,因为这样做不会将关系存储在relations 属性中。不过,我刚刚想出了解决这个问题的方法,如果您在我发布答案时请耐心等待。非常感谢您的建议。
    【解决方案2】:

    我已经设法解决了这个问题。原来我有这样的东西:

    class MyModel extends Model
    {
        public function relationship()
        {
            return $this->hasMany('App\Related');
        }
    }
    
    class Related extends Model
    {
        public function subrelated()
        {
            return $this->belongsTo('App\Subrelated');
        }
    }
    
    class Subrelated extends Model
    {
    
    }
    

    在 Laravel 源码中大量挖掘之后,我发现当你使用魔术方法(即__get())调用一个像属性一样的关系时,Laravel 将它存储在模型$relations 属性中以供使用之后。考虑到这一点,我向MyModel 添加了另一种方法,如下所示:

    class MyModel extends Model
    {
        public function relationship()
        {
            return $this->hasMany('App\Related');
        }
    
        public function relationshipWithSubrelated()
        {
            return $this->relationship()->with('subrelated');
        }
    }
    

    现在我可以根据需要多次调用如下关系,它总是给我相同的结果:

    $myModel = MyModel::find(1);
    $myModel->relationshipWithSubrelated;
    

    希望我在花费数小时尝试解决问题之前想到它!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多