I am currently running on Laravel 5.1.19 and am observing the following issue:
Assume the following models (Students and Teachers as example):
class Teacher extends \Illuminate\Database\Eloquent\Model {
public function rel_students() {
return $this->hasMany(Student::class);
}
}
class Student extends \Illuminate\Database\Eloquent\Model {
public function rel_teacher() {
return $this->belongsTo(Teacher::class);
}
}
If you then query an instance of Teacher
and (lazy) eager load its Student
s, the ->rel_teacher
magic member of all students triggers a fresh query against that teacher:
$teacher = Teacher::with('rel_students')->find(1);
// or this, the effect is the same
$teacher = Teacher::find(1); $teacher->load('rel_students');
foreach ($teacher->rel_students as $student)
echo $student->teacher->name . "<br>";
Query log for the above code:
SELECT * FROM teachers WHERE id = 1
SELECT * FROM students WHERE teacher_id IN (1)
SELECT * FROM teachers WHERE id = 1
SELECT * FROM teachers WHERE id = 1 # what is going on here?!
SELECT * FROM teachers WHERE id = 1
... and so forth for every student ...
The issue: Eloquent has an instance of Teacher
#1 when find(1)
finishes. I expect eloquent to pass a reference to this very PHP-Objet to the eager loaded Student
s so that $student->teacher
returns that reference instead of triggering another query.
another, negative sideffect: even if the queries were no performance issue (which they are!) i'd have hundrets of instances for Teacher
#1 fyling around and that is very contrary to the unit-of-work pattern.
Question: What can i do to make eloquent behave as i expect it to? if not possible: which of the PHP-ORMs are "intelligent" enough and do this simple trick?
UPDATE 1: I did try to remove the underscore from the relation name - same result.
UPDATE 2: As mentioned in an answer, with
/load
with both relations does a almost-perfect job: $teacher->load('rel_students.rel_teacher')
. This reduces the queries for the teacher to 2, but that still leaves the issue that $student->rel_teacher !== $teacher
. This is fine until someone modifies either object and then it starts getting hairy.
If you want to reference the teacher from students you need to lazy load them as well for each student so with('rel_students.rel_teacher')
and then echo $student->rel_teacher->name
OR use the teacher model you are looping on echo $teacher->name
EDIT
$teacher = Teacher::with('rel_students.rel_teacher')->find(1);
foreach($teacher->rel_students as $student)
{
if($teacher->id != $student->rel_teacher->id)
{
dd('error');
}
echo $student->rel_teacher->name . "<br>";
}