Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

slaughter550's avatar

Nested Method Eager Loading

I have a model that looks like this.

public function assessment()
{
    return $this->question->assessment()->withTrashed();
}

public function question()
{
    return $this->belongsTo("App\Models\AssessmentQuestion", 'question_id')->withTrashed();
}

and I was wondering why this works

$this->load('question', 'question.assessment');
info($this->question->assessment);

but this does not

$this->load('question', 'assessment');
info($this->assessment);

Or if there is just a better way to do this?

Thanks! -Alex

0 likes
10 replies
slaughter550's avatar

@Corez64 I'm aware of the hasManyThough method however I am looking for a technical explanation since we have some elements that are 4-6 layers deep. I'm wondering if there is a reason eager/lazy loading does not work when there is a reference to another eager/lazy loaded object in the loaded method. I am also wondering if there is a way to fix this behavior

pmall's avatar

This is a nonsense :

public function assessment()
{
    return $this->question->assessment()->withTrashed();
}

I guess eager loading query first, then associate objects. So at the time it try to get the query for assessments, it query the question (with $this->question), total mindfuck, cant work.

I don't understand why $this->load('question.assessment'); is not suited to your problem. It load the questions, then the assessments of these questions, and it can go on like this any depth level you want.

PavanKataria's avatar

@slaughter550 Great question. Allow me to answer it:

Why does this work

$this->load('question', 'question.assessment'); 

while this does not work.

$this->load('question', 'assessment');

This is because when you query on your model, you have to eager load relative to that model. There is no relationship defined between assessment and your model which is why you're not able to lazily eager load them. Now when you do question , that works because there is indeed a relationship defined called question on your model class. You can also eager load nested relationships using dot notation. Because question.assessment works you can be sure that there is an assessment relationship defined belong to the question model.

So you're technically eager loading the grandchildren belonging to the question model which belongs to the parent model which you referred to as model.

Just so you know, you don't have to eager load, questions seperately if you're already eager loading question.assessment, since you can't eager load the grandchild assessment without eager loading it's parent called: question.

Pavan

2 likes
JarekTkaczyk's avatar
Level 53

@PavanKataria I love the way you answered that :) However, you didn't really answer the question, since the OP does define that as a relation (technically valid relation).

@slaughter550 First, let me say, that your code works, but obviously doesn't do what you expect it to. Next thing - hasManyThrough wouldn't work here, since it is belongsTo (and I assume the same for question-assessment relation).

Now, the reason that your pseudo-relation can't be used while eager loading, is because of the way eager loading works internally, as opposed to loading something on the instance:

$model->assessment; // gets everything forom the instances' attributes
$model->load('assessment'); // gets everything from the queried collections

It means that the latter gets a collection of related models, then runs another query for each nested relation with a where id in (...) clause. in your case there is no id for the where in part, since you have no assessment_id on your $model.

Eventually your code really worked and queried assessments table, but with a where id in (0) clause, that's why you get no results.

Like already mentioned by @pmall - such relation is not gonna work the eloquent way, so I suggest you don't use it as one. You can do this instead:

// accessor immitating relation's dynamic property:
public function getAssessmentAttribute()
{
    return ($this->question) ? $this->question->assessment : null;
}

// then
$model->load('question.assessment');
$model->assessment;

Finally, read this http://softonsofa.com/laravel-querying-any-level-far-relations-with-simple-trick/

2 likes
PavanKataria's avatar

@JarekTkaczyk Could have liked it ;) Anyway, jokes aside, I'm not sure what happened, no sleep has caused me to have a brain freeze! Momentarily stuck in a while loop! I answered his initial generic question but didn't deal with his problem! lol.

Well I'm glad you did anyway. :) Edit: Ah, I've just had another look at the OP's question, and one of his relations is a pseudo relation... lol as you mentioned. Lol, i missed that! I instantily assumed a proper relationship definition ;p

pmall's avatar

@JarekTkaczyk sometimes I think it wont be that difficult to allow developer to define its own relationships classes and use it. For example I have a particular project when I would need to return only one related model from a belongsToMany relationship (big db from analyzing a lot of data, this is a pivot table just linking one table entry to only one entry from another table, dumb but easiest table structure for this analysis - I should have user a graph db). In fact it would be a belongsToMany relationship using first() instead of get(). What do you think about this ?

I know I can do it by defining the relationship and use an accessor to get the first item of the collection but I try to look for a prettiest solution :)

JarekTkaczyk's avatar

@pmall Sure you can. I've used eg. belongsToThrough and others this way:

  1. create a trait with yourRelationMethod implementation and use it in the model
  2. create the YourRelation class, that extends Relation (or any of the existing ones)

If you have more of such relations, then probably it would be wise to add them to your BaseModel instead of using traits.

pmall's avatar

@JarekTkaczyk Yes I did this with the trait and the relationship class. Sadly I've had to copy paste all the belongsToMany model's method into my belongsToOne method. I through everything happened in the constructor method of BelongsToMany. I hope someday it will be a bit improved so it is easier to extends relationship class.

Please or to participate in this conversation.