可以在Laravel Eloquent中使用eagerload数组而不是模型吗?

时间:2022-10-16 08:20:37

Is it possible to eager load an array of key => values using eloquent's with() or load() functions instead of a collection of Model objects? I only need a couple values from the relations, and don't need any object functionality, so I really don't need the memory overhead of having

是否可以使用eloquent的with()或load()函数而不是模型对象的集合来加载一个key =>值数组?我只需要关系中的几个值,并且不需要任何对象功能,所以我真的不需要内存开销

"relations" => [
    "relationship" => Collection()
        "items" => [
            0 => Model()
                "attributes" => ...
                "table" => ...
                "guarded" => ...
                ...etc
            1 => Model(),
            ...etc

if I could have something more like

如果我能有更多的东西

"relations" => [
    "relationship" => [
        "items" => [
            0 => [key => val, key => val ...],
            1 => [key => val, key => val ...],
            ...etc

Is this possible? Or even use a basic php object instead of an Eloquent Model which contains an insane amount of overhead for just needing a few values? I have looked through the API, and stepped through a call stack while building the query builder object, and I don't see any obvious means of doing this, but I'm curious if any one knows a workaround.

这可能吗?或者甚至使用一个基本的php对象而不是一个Eloquent Model,其中包含一些疯狂的开销,只需要几个值?我查看了API,并在构建查询构建器对象时逐步调试了一个调用堆栈,我没有看到任何明显的方法来执行此操作,但我很好奇是否有人知道解决方法。

Thanks!

-Eric

2 个解决方案

#1


The bit about eager loading threw me off. That implies that you would like to load a bunch of models nested within a bunch of models ie:

关于急切装载的一点让我失望。这意味着你想加载嵌套在一堆模型中的一堆模型,即:

// Returns 1000 Foo's with all their bars in two queries.
Foo::take(1000)->with('bars')->get();

Considering that your issue is hitting the memory limit, I doubt you actually want to invoke eager loading this way.

考虑到你的问题是达到内存限制,我怀疑你真的想以这种方式调用急切加载。

Directly access query builder

直接访问查询构建器

I think you are looking for a way to query the relationship without hydrating models. You can do this by accessing the query builder directly.

我认为你正在寻找一种方法来查询关系而不保湿模型。您可以通过直接访问查询构建器来完成此操作。

$foo = Foo::find($id);

// Returns hydrated models
$bars = $foo->bars()->take(1000)->get();

// Returns raw data
$bars = $foo->bars()->take(1000)->getQuery()->get();

Default eager loading

默认急切加载

Eager loading can be set up on the model directly, causing certain relationships to always be loaded with the model.

可以直接在模型上设置急切加载,从而导致始终为模型加载某些关系。

// Class Bar
protected $with = ['awks'];

If this were the case, the 1000 Bars would automatically load all their Awks. In your situation that would make things exponentially worse. Here is a happy medium that allows you to avoid eager loading nested relationships while still having the benefit of models.

如果是这种情况,1000 Bars将自动加载所有Awks。在你的情况下会使事情呈指数级恶化。这是一个快乐的媒介,允许您避免急切加载嵌套关系,同时仍然有模型的好处。

// Returns hydrated models without eager loading any relationships
$bars = Foo::find($id)->bars()->take(1000)->getModels();

#2


Ultimately I came up with a different solution than what I thought I was looking for. Since I only truly needed a sum of the data that was being loaded with the relationships, I discovered that aggregates can be eager loaded.

最终,我想出了一个与我想要的不同的解决方案。由于我只需要加载关系的数据的总和,我发现聚合可以被急切加载。

The general gist of my problem was this: We have customers. They have customers (debtors). These debtors have a balance that is a sum of a lot of different models (payments, invoices, credit notes, plus some others). I wanted to be able to call debtor->balance, which would be a sum of all these things. This would be easy enough to do hard coding the query for the balance behind this function, but then I would run into an n+1 problem when doing

我的问题的一般要点是:我们有客户。他们有客户(债务人)。这些债务人的余额是许多不同模型(付款,发票,信用票据以及其他一些模型)的总和。我希望能够调用debtor-> balance,这将是所有这些事情的总和。这很容易对查询进行硬编码以实现此函数的平衡,但是在执行时我会遇到n + 1问题

$customer = Customer::find(###)
foreach($customer->debtors as $debtor)
{
    $debtor->balance();
}

The original solution, which prompted this question, was to have the balance function iterate through all the components that composed the balance, and add them all. Thus, by eager loading all of these components, I could do $debtor->balance() or $customer->debtor->balance() with eager loading, and everything seemed fine. You can see how if a customer has 2000 debtors, and that debtor may have dozens of invoices, each with 1 or more payments, this becomes a giant amount of data to load. By restructuring the eager loads to only load an aggregate value, each debtor only had to load 5 relationships, each with a single item in them, bringing it well within memory limits.

提出这个问题的原始解决方案是让balance函数迭代组成余额的所有组件,并将它们全部添加。因此,通过急切加载所有这些组件,我可以通过急切加载来进行$ debtor-> balance()或$ customer-> debtor-> balance(),一切似乎都很好。您可以看到如果客户有2000个债务人,并且债务人可能有几十张发票,每个都有一个或多个付款,这就变成了要加载的大量数据。通过重组急切负载以仅加载聚合值,每个债务人只需加载5个关系,每个关系中包含一个项目,使其在内存限制内完好无损。

#1


The bit about eager loading threw me off. That implies that you would like to load a bunch of models nested within a bunch of models ie:

关于急切装载的一点让我失望。这意味着你想加载嵌套在一堆模型中的一堆模型,即:

// Returns 1000 Foo's with all their bars in two queries.
Foo::take(1000)->with('bars')->get();

Considering that your issue is hitting the memory limit, I doubt you actually want to invoke eager loading this way.

考虑到你的问题是达到内存限制,我怀疑你真的想以这种方式调用急切加载。

Directly access query builder

直接访问查询构建器

I think you are looking for a way to query the relationship without hydrating models. You can do this by accessing the query builder directly.

我认为你正在寻找一种方法来查询关系而不保湿模型。您可以通过直接访问查询构建器来完成此操作。

$foo = Foo::find($id);

// Returns hydrated models
$bars = $foo->bars()->take(1000)->get();

// Returns raw data
$bars = $foo->bars()->take(1000)->getQuery()->get();

Default eager loading

默认急切加载

Eager loading can be set up on the model directly, causing certain relationships to always be loaded with the model.

可以直接在模型上设置急切加载,从而导致始终为模型加载某些关系。

// Class Bar
protected $with = ['awks'];

If this were the case, the 1000 Bars would automatically load all their Awks. In your situation that would make things exponentially worse. Here is a happy medium that allows you to avoid eager loading nested relationships while still having the benefit of models.

如果是这种情况,1000 Bars将自动加载所有Awks。在你的情况下会使事情呈指数级恶化。这是一个快乐的媒介,允许您避免急切加载嵌套关系,同时仍然有模型的好处。

// Returns hydrated models without eager loading any relationships
$bars = Foo::find($id)->bars()->take(1000)->getModels();

#2


Ultimately I came up with a different solution than what I thought I was looking for. Since I only truly needed a sum of the data that was being loaded with the relationships, I discovered that aggregates can be eager loaded.

最终,我想出了一个与我想要的不同的解决方案。由于我只需要加载关系的数据的总和,我发现聚合可以被急切加载。

The general gist of my problem was this: We have customers. They have customers (debtors). These debtors have a balance that is a sum of a lot of different models (payments, invoices, credit notes, plus some others). I wanted to be able to call debtor->balance, which would be a sum of all these things. This would be easy enough to do hard coding the query for the balance behind this function, but then I would run into an n+1 problem when doing

我的问题的一般要点是:我们有客户。他们有客户(债务人)。这些债务人的余额是许多不同模型(付款,发票,信用票据以及其他一些模型)的总和。我希望能够调用debtor-> balance,这将是所有这些事情的总和。这很容易对查询进行硬编码以实现此函数的平衡,但是在执行时我会遇到n + 1问题

$customer = Customer::find(###)
foreach($customer->debtors as $debtor)
{
    $debtor->balance();
}

The original solution, which prompted this question, was to have the balance function iterate through all the components that composed the balance, and add them all. Thus, by eager loading all of these components, I could do $debtor->balance() or $customer->debtor->balance() with eager loading, and everything seemed fine. You can see how if a customer has 2000 debtors, and that debtor may have dozens of invoices, each with 1 or more payments, this becomes a giant amount of data to load. By restructuring the eager loads to only load an aggregate value, each debtor only had to load 5 relationships, each with a single item in them, bringing it well within memory limits.

提出这个问题的原始解决方案是让balance函数迭代组成余额的所有组件,并将它们全部添加。因此,通过急切加载所有这些组件,我可以通过急切加载来进行$ debtor-> balance()或$ customer-> debtor-> balance(),一切似乎都很好。您可以看到如果客户有2000个债务人,并且债务人可能有几十张发票,每个都有一个或多个付款,这就变成了要加载的大量数据。通过重组急切负载以仅加载聚合值,每个债务人只需加载5个关系,每个关系中包含一个项目,使其在内存限制内完好无损。