Laravel 5:Lazy Eager加载而不重复查询

时间:2016-05-27 11:24:25

标签: php laravel orm laravel-5 eloquent

在我的项目中,我正在我的一个模型上编写一个方法,该方法使用其中一个关系和一个子关系,所以我不得不使用懒惰的急切加载:

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的形式存储在子模型中。

干杯

2 个答案:

答案 0 :(得分: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()

答案 1 :(得分:1)

我已经成功解决了这个问题。最初我有这样的事情:

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会将其存储在模型中{{1}稍后使用的属性。考虑到这一点,我向$relations添加了另一种方法:

MyModel

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

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

    public function relationshipWithSubrelated()
    {
        return $this->relationship()->with('subrelated');
    }
}

在我花了好几个小时试图解决之前,我想到了它!