0

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

"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.

Thanks!

-Eric

EpicWally
  • 287
  • 3
  • 14
  • Not really. However you might be able to use [joins](http://laravel.com/docs/5.0/queries#joins) for what you want. – lukasgeiter May 13 '15 at 19:20
  • Unless you are loading ten's of thousands of records (maybe even hundreds of thousands) - the reality is you are wasting your time. The memory overhead is not going to have any noticeable impact on your applications. – Laurence May 14 '15 at 01:16
  • I am, actually, loading a very large number of records. Easily in the tens of thousands range, and I wouldn't be surprised if in some instances it might get to the 100s of thousands range. I am running into a memory overflow error, forcing me to chunk the results in very small chunks, causing a large number of queries. I was hoping cutting these back to basic arrays would allow me to significantly increase my chunk size. – EpicWally May 14 '15 at 11:55

2 Answers2

0

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.

// Returns hydrated models without eager loading any relationships
$bars = Foo::find($id)->bars()->take(1000)->getModels();
Collin James
  • 9,062
  • 2
  • 28
  • 36
0

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

$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.

EpicWally
  • 287
  • 3
  • 14