Skip to content

Commit b6a2af2

Browse files
authored
[12.x] Support nested relations on relationLoaded method (#55471)
* relationLoaded method in Model now supports nested relations * Removed argument type hint and return type * Improved readability and added edge case safety * Improved performance
1 parent 3073c3a commit b6a2af2

File tree

2 files changed

+190
-1
lines changed

2 files changed

+190
-1
lines changed

src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,28 @@ public function getRelation($relation)
10721072
*/
10731073
public function relationLoaded($key)
10741074
{
1075-
return array_key_exists($key, $this->relations);
1075+
if (array_key_exists($key, $this->relations)) {
1076+
return true;
1077+
}
1078+
1079+
[$relation, $nestedRelation] = array_replace(
1080+
[null, null],
1081+
explode('.', $key, 2),
1082+
);
1083+
1084+
if (! array_key_exists($relation, $this->relations)) {
1085+
return false;
1086+
}
1087+
1088+
if ($nestedRelation !== null) {
1089+
foreach ($this->$relation as $related) {
1090+
if (! $related->relationLoaded($nestedRelation)) {
1091+
return false;
1092+
}
1093+
}
1094+
}
1095+
1096+
return true;
10761097
}
10771098

10781099
/**
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Database\EloquentModelRelationLoadedTest;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
7+
use Illuminate\Database\Eloquent\Relations\HasMany;
8+
use Illuminate\Database\Schema\Blueprint;
9+
use Illuminate\Support\Facades\Schema;
10+
use Illuminate\Tests\Integration\Database\DatabaseTestCase;
11+
12+
class EloquentModelRelationLoadedTest extends DatabaseTestCase
13+
{
14+
protected function afterRefreshingDatabase()
15+
{
16+
Schema::create('ones', function (Blueprint $table) {
17+
$table->increments('id');
18+
});
19+
20+
Schema::create('twos', function (Blueprint $table) {
21+
$table->increments('id');
22+
$table->integer('one_id');
23+
});
24+
25+
Schema::create('threes', function (Blueprint $table) {
26+
$table->increments('id');
27+
$table->integer('two_id');
28+
$table->integer('one_id')->nullable();
29+
});
30+
}
31+
32+
public function testWhenRelationIsInvalid()
33+
{
34+
$one = One::query()->create();
35+
$one->twos()->create();
36+
37+
$model = One::query()
38+
->with('twos')
39+
->find($one->id);
40+
41+
$this->assertFalse($model->relationLoaded('.'));
42+
$this->assertFalse($model->relationLoaded('invalid'));
43+
}
44+
45+
public function testWhenNestedRelationIsInvalid()
46+
{
47+
$one = One::query()->create();
48+
$one->twos()->create();
49+
50+
$model = One::query()
51+
->with('twos')
52+
->find($one->id);
53+
54+
$this->assertFalse($model->relationLoaded('twos.'));
55+
$this->assertFalse($model->relationLoaded('twos.invalid'));
56+
}
57+
58+
public function testWhenRelationNotLoaded()
59+
{
60+
$one = One::query()->create();
61+
$one->twos()->create();
62+
63+
$model = One::query()->find($one->id);
64+
65+
$this->assertFalse($model->relationLoaded('twos'));
66+
}
67+
68+
public function testWhenRelationLoaded()
69+
{
70+
$one = One::query()->create();
71+
$one->twos()->create();
72+
73+
$model = One::query()
74+
->with('twos')
75+
->find($one->id);
76+
77+
$this->assertTrue($model->relationLoaded('twos'));
78+
}
79+
80+
public function testWhenChildRelationIsNotLoaded()
81+
{
82+
$one = One::query()->create();
83+
$two = $one->twos()->create();
84+
$two->threes()->create();
85+
86+
$model = One::query()
87+
->with('twos')
88+
->find($one->id);
89+
90+
$this->assertTrue($model->relationLoaded('twos'));
91+
$this->assertFalse($model->relationLoaded('twos.threes'));
92+
}
93+
94+
public function testWhenChildRelationIsLoaded()
95+
{
96+
$one = One::query()->create();
97+
$two = $one->twos()->create();
98+
$two->threes()->create();
99+
100+
$model = One::query()
101+
->with('twos.threes')
102+
->find($one->id);
103+
104+
$this->assertTrue($model->relationLoaded('twos'));
105+
$this->assertTrue($model->relationLoaded('twos.threes'));
106+
}
107+
108+
public function testWhenChildRecursiveRelationIsLoaded()
109+
{
110+
$one = One::query()->create();
111+
$two = $one->twos()->create();
112+
$two->threes()->create(['one_id' => $one->id]);
113+
114+
$model = One::query()
115+
->with('twos.threes.one')
116+
->find($one->id);
117+
118+
$this->assertTrue($model->relationLoaded('twos'));
119+
$this->assertTrue($model->relationLoaded('twos.threes'));
120+
$this->assertTrue($model->relationLoaded('twos.threes.one'));
121+
}
122+
}
123+
124+
class One extends Model
125+
{
126+
public $table = 'ones';
127+
public $timestamps = false;
128+
protected $guarded = [];
129+
130+
public function twos(): HasMany
131+
{
132+
return $this->hasMany(Two::class, 'one_id');
133+
}
134+
}
135+
136+
class Two extends Model
137+
{
138+
public $table = 'twos';
139+
public $timestamps = false;
140+
protected $guarded = [];
141+
142+
public function one(): BelongsTo
143+
{
144+
return $this->belongsTo(One::class, 'one_id');
145+
}
146+
147+
public function threes(): HasMany
148+
{
149+
return $this->hasMany(Three::class, 'two_id');
150+
}
151+
}
152+
153+
class Three extends Model
154+
{
155+
public $table = 'threes';
156+
public $timestamps = false;
157+
protected $guarded = [];
158+
159+
public function one(): BelongsTo
160+
{
161+
return $this->belongsTo(One::class, 'one_id');
162+
}
163+
164+
public function two(): BelongsTo
165+
{
166+
return $this->belongsTo(Two::class, 'two_id');
167+
}
168+
}

0 commit comments

Comments
 (0)