Skip to content

Commit 4d25fdc

Browse files
[10.x] Optimize eager loading when no keys to be loaded (#43724)
* Optimize eager loading when no keys to be loaded * Add eagerImplicitlyEmpty parameter and docblock comment. * Remove unneeded default in if statement for eagerImplicitlyEmpty * formatting Co-authored-by: Taylor Otwell <[email protected]>
1 parent fd1effe commit 4d25fdc

File tree

6 files changed

+63
-7
lines changed

6 files changed

+63
-7
lines changed

src/Illuminate/Database/Eloquent/Relations/BelongsTo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function addEagerConstraints(array $models)
113113

114114
$whereIn = $this->whereInMethod($this->related, $this->ownerKey);
115115

116-
$this->query->{$whereIn}($key, $this->getEagerModelKeys($models));
116+
$this->whereInEager($whereIn, $key, $this->getEagerModelKeys($models));
117117
}
118118

119119
/**

src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ public function addEagerConstraints(array $models)
242242
{
243243
$whereIn = $this->whereInMethod($this->parent, $this->parentKey);
244244

245-
$this->query->{$whereIn}(
245+
$this->whereInEager(
246+
$whereIn,
246247
$this->getQualifiedForeignPivotKeyName(),
247248
$this->getKeys($models, $this->parentKey)
248249
);

src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,10 @@ public function addEagerConstraints(array $models)
160160
{
161161
$whereIn = $this->whereInMethod($this->farParent, $this->localKey);
162162

163-
$this->query->{$whereIn}(
164-
$this->getQualifiedFirstKeyName(), $this->getKeys($models, $this->localKey)
163+
$this->whereInEager(
164+
$whereIn,
165+
$this->getQualifiedFirstKeyName(),
166+
$this->getKeys($models, $this->localKey)
165167
);
166168
}
167169

src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,11 @@ public function addEagerConstraints(array $models)
9898
{
9999
$whereIn = $this->whereInMethod($this->parent, $this->localKey);
100100

101-
$this->getRelationQuery()->{$whereIn}(
102-
$this->foreignKey, $this->getKeys($models, $this->localKey)
101+
$this->whereInEager(
102+
$whereIn,
103+
$this->foreignKey,
104+
$this->getKeys($models, $this->localKey),
105+
$this->getRelationQuery()
103106
);
104107
}
105108

src/Illuminate/Database/Eloquent/Relations/Relation.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ abstract class Relation implements BuilderContract
4040
*/
4141
protected $related;
4242

43+
/**
44+
* Indicates whether the eagerly loaded relation should implicitly return an empty collection.
45+
*
46+
* @var bool
47+
*/
48+
protected $eagerKeysWereEmpty = false;
49+
4350
/**
4451
* Indicates if the relation is adding constraints.
4552
*
@@ -154,7 +161,9 @@ abstract public function getResults();
154161
*/
155162
public function getEager()
156163
{
157-
return $this->get();
164+
return $this->eagerKeysWereEmpty
165+
? $this->query->getModel()->newCollection()
166+
: $this->get();
158167
}
159168

160169
/**
@@ -377,6 +386,24 @@ public function relatedUpdatedAt()
377386
return $this->related->getUpdatedAtColumn();
378387
}
379388

389+
/**
390+
* Add a whereIn eager constraint for the given set of model keys to be loaded.
391+
*
392+
* @param string $whereIn
393+
* @param string $key
394+
* @param array $modelKeys
395+
* @param \Illuminate\Database\Eloquent\Builder $query
396+
* @return void
397+
*/
398+
protected function whereInEager(string $whereIn, string $key, array $modelKeys, $query = null)
399+
{
400+
($query ?? $this->query)->{$whereIn}($key, $modelKeys);
401+
402+
if ($modelKeys === []) {
403+
$this->eagerKeysWereEmpty = true;
404+
}
405+
}
406+
380407
/**
381408
* Get the name of the "where in" method for eager loading.
382409
*

tests/Database/DatabaseEloquentBuilderTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,29 @@ public function testRelationshipEagerLoadProcess()
767767
unset($_SERVER['__eloquent.constrain']);
768768
}
769769

770+
public function testRelationshipEagerLoadProcessForImplicitlyEmpty()
771+
{
772+
$queryBuilder = $this->getMockQueryBuilder();
773+
$builder = m::mock(Builder::class.'[getRelation]', [$queryBuilder]);
774+
$builder->setEagerLoads(['parentFoo' => function ($query) {
775+
$_SERVER['__eloquent.constrain'] = $query;
776+
}]);
777+
$model = new EloquentBuilderTestModelSelfRelatedStub;
778+
$this->mockConnectionForModel($model, 'SQLite');
779+
780+
$models = [
781+
new EloquentBuilderTestModelSelfRelatedStub,
782+
new EloquentBuilderTestModelSelfRelatedStub,
783+
];
784+
$relation = m::mock($model->parentFoo());
785+
786+
$builder->shouldReceive('getRelation')->once()->with('parentFoo')->andReturn($relation);
787+
788+
$results = $builder->eagerLoadRelations($models);
789+
790+
unset($_SERVER['__eloquent.constrain']);
791+
}
792+
770793
public function testGetRelationProperlySetsNestedRelationships()
771794
{
772795
$builder = $this->getBuilder();

0 commit comments

Comments
 (0)