Skip to content

Commit 223c4bc

Browse files
authored
[9.x] Address Dynamic Relation Resolver inconsiency issue with extended Models (#45122)
* #44741 - Address Dynamic Relation Resolver inconsistency issue with extended Models * style fixes
1 parent 26941b0 commit 223c4bc

File tree

5 files changed

+56
-5
lines changed

5 files changed

+56
-5
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ public function isRelation($key)
541541
}
542542

543543
return method_exists($this, $key) ||
544-
(static::$relationResolvers[get_class($this)][$key] ?? null);
544+
$this->relationResolver(static::class, $key);
545545
}
546546

547547
/**

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ trait HasRelationships
5454
*/
5555
protected static $relationResolvers = [];
5656

57+
/**
58+
* Get the dynamic relation resolver if defined or inherited, or return null.
59+
*
60+
* @param string $class
61+
* @param string $key
62+
* @return mixed
63+
*/
64+
public function relationResolver($class, $key)
65+
{
66+
if ($resolver = static::$relationResolvers[$class][$key] ?? null) {
67+
return $resolver;
68+
}
69+
70+
if ($parent = get_parent_class($class)) {
71+
return $this->relationResolver($parent, $key);
72+
}
73+
74+
return null;
75+
}
76+
5777
/**
5878
* Define a dynamic relation resolver.
5979
*

src/Illuminate/Database/Eloquent/Model.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2314,7 +2314,7 @@ public function __call($method, $parameters)
23142314
return $this->$method(...$parameters);
23152315
}
23162316

2317-
if ($resolver = (static::$relationResolvers[get_class($this)][$method] ?? null)) {
2317+
if ($resolver = ($this->relationResolver(static::class, $method))) {
23182318
return $resolver($this);
23192319
}
23202320

tests/Database/DatabaseEloquentBelongsToManyWithCastedAttributesTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ public function testModelsAreProperlyMatchedToParents()
2525
$model1->shouldReceive('hasGetMutator')->andReturn(false);
2626
$model1->shouldReceive('hasAttributeMutator')->andReturn(false);
2727
$model1->shouldReceive('getCasts')->andReturn([]);
28-
$model1->shouldReceive('getRelationValue', 'relationLoaded', 'setRelation', 'isRelation')->passthru();
28+
$model1->shouldReceive('getRelationValue', 'relationLoaded', 'relationResolver', 'setRelation', 'isRelation')->passthru();
2929

3030
$model2 = m::mock(Model::class);
3131
$model2->shouldReceive('getAttribute')->with('parent_key')->andReturn(2);
3232
$model2->shouldReceive('getAttribute')->with('foo')->passthru();
3333
$model2->shouldReceive('hasGetMutator')->andReturn(false);
3434
$model2->shouldReceive('hasAttributeMutator')->andReturn(false);
3535
$model2->shouldReceive('getCasts')->andReturn([]);
36-
$model2->shouldReceive('getRelationValue', 'relationLoaded', 'setRelation', 'isRelation')->passthru();
36+
$model2->shouldReceive('getRelationValue', 'relationLoaded', 'relationResolver', 'setRelation', 'isRelation')->passthru();
3737

3838
$result1 = (object) [
3939
'pivot' => (object) [

tests/Database/DatabaseEloquentDynamicRelationsTest.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function testBasicDynamicRelations()
2020
$this->assertEquals(['many' => 'related'], $model->getRelationValue('dynamicRel_2'));
2121
}
2222

23-
public function testDynamicRelationsOverride()
23+
public function testBasicDynamicRelationsOverride()
2424
{
2525
// Dynamic Relations can override each other.
2626
DynamicRelationModel::resolveRelationUsing('dynamicRelConflict', fn ($m) => $m->hasOne(Related::class));
@@ -33,6 +33,32 @@ public function testDynamicRelationsOverride()
3333
$this->assertTrue($model->isRelation('dynamicRelConflict'));
3434
}
3535

36+
public function testInharitedDynamicRelations()
37+
{
38+
DynamicRelationModel::resolveRelationUsing('inheritedDynamicRel', fn () => new FakeHasManyRel);
39+
$model = new DynamicRelationModel;
40+
$model2 = new DynamicRelationModel2;
41+
$model4 = new DynamicRelationModel4;
42+
$this->assertTrue($model->isRelation('inheritedDynamicRel'));
43+
$this->assertTrue($model4->isRelation('inheritedDynamicRel'));
44+
$this->assertFalse($model2->isRelation('inheritedDynamicRel'));
45+
$this->assertEquals($model->inheritedDynamicRel(), $model4->inheritedDynamicRel());
46+
$this->assertEquals($model->inheritedDynamicRel, $model4->inheritedDynamicRel);
47+
}
48+
49+
public function testInheritedDynamicRelationsOverride()
50+
{
51+
// Inherited Dynamic Relations can be overriden
52+
DynamicRelationModel::resolveRelationUsing('dynamicRelConflict', fn ($m) => $m->hasOne(Related::class));
53+
$model = new DynamicRelationModel;
54+
$model4 = new DynamicRelationModel4;
55+
$this->assertInstanceOf(HasOne::class, $model->dynamicRelConflict());
56+
$this->assertInstanceOf(HasOne::class, $model4->dynamicRelConflict());
57+
DynamicRelationModel4::resolveRelationUsing('dynamicRelConflict', fn ($m) => $m->hasMany(Related::class));
58+
$this->assertInstanceOf(HasOne::class, $model->dynamicRelConflict());
59+
$this->assertInstanceOf(HasMany::class, $model4->dynamicRelConflict());
60+
}
61+
3662
public function testDynamicRelationsCanNotHaveTheSameNameAsNormalRelations()
3763
{
3864
$model = new DynamicRelationModel;
@@ -94,6 +120,11 @@ class DynamicRelationModel3 extends Model
94120
//
95121
}
96122

123+
class DynamicRelationModel4 extends DynamicRelationModel
124+
{
125+
//
126+
}
127+
97128
class FakeHasManyRel extends HasMany
98129
{
99130
public function __construct()

0 commit comments

Comments
 (0)