Skip to content

Commit 702cb88

Browse files
authored
[11.x] Fixes through() relationship (#52318)
* Adds a failing test * Allows MorphOneOrMany in through() method
1 parent 9c57afc commit 702cb88

File tree

2 files changed

+140
-10
lines changed

2 files changed

+140
-10
lines changed

src/Illuminate/Database/Eloquent/PendingHasThroughRelationship.php

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use BadMethodCallException;
66
use Illuminate\Database\Eloquent\Relations\HasMany;
7+
use Illuminate\Database\Eloquent\Relations\MorphMany;
8+
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
79
use Illuminate\Support\Str;
810

911
/**
@@ -44,7 +46,7 @@ public function __construct($rootModel, $localRelationship)
4446
*
4547
* @template TRelatedModel of \Illuminate\Database\Eloquent\Model
4648
*
47-
* @param string|(callable(TIntermediateModel): (\Illuminate\Database\Eloquent\Relations\HasOne<TRelatedModel, TIntermediateModel>|\Illuminate\Database\Eloquent\Relations\HasMany<TRelatedModel, TIntermediateModel>)) $callback
49+
* @param string|(callable(TIntermediateModel): (\Illuminate\Database\Eloquent\Relations\HasOne<TRelatedModel, TIntermediateModel>|\Illuminate\Database\Eloquent\Relations\HasMany<TRelatedModel, TIntermediateModel>|\Illuminate\Database\Eloquent\Relations\MorphOneOrMany<TRelatedModel, TIntermediateModel>)) $callback
4850
* @return (
4951
* $callback is string
5052
* ? \Illuminate\Database\Eloquent\Relations\HasManyThrough<\Illuminate\Database\Eloquent\Model, TIntermediateModel, TDeclaringModel>|\Illuminate\Database\Eloquent\Relations\HasOneThrough<\Illuminate\Database\Eloquent\Model, TIntermediateModel, TDeclaringModel>
@@ -64,24 +66,30 @@ public function has($callback)
6466
$distantRelation = $callback($this->localRelationship->getRelated());
6567

6668
if ($distantRelation instanceof HasMany) {
67-
return $this->rootModel->hasManyThrough(
69+
$returnedRelation = $this->rootModel->hasManyThrough(
6870
$distantRelation->getRelated()::class,
6971
$this->localRelationship->getRelated()::class,
7072
$this->localRelationship->getForeignKeyName(),
7173
$distantRelation->getForeignKeyName(),
7274
$this->localRelationship->getLocalKeyName(),
7375
$distantRelation->getLocalKeyName(),
7476
);
77+
} else {
78+
$returnedRelation = $this->rootModel->hasOneThrough(
79+
$distantRelation->getRelated()::class,
80+
$this->localRelationship->getRelated()::class,
81+
$this->localRelationship->getForeignKeyName(),
82+
$distantRelation->getForeignKeyName(),
83+
$this->localRelationship->getLocalKeyName(),
84+
$distantRelation->getLocalKeyName(),
85+
);
86+
}
87+
88+
if ($this->localRelationship instanceof MorphOneOrMany) {
89+
$returnedRelation->where($this->localRelationship->getQualifiedMorphType(), $this->localRelationship->getMorphClass());
7590
}
7691

77-
return $this->rootModel->hasOneThrough(
78-
$distantRelation->getRelated()::class,
79-
$this->localRelationship->getRelated()::class,
80-
$this->localRelationship->getForeignKeyName(),
81-
$distantRelation->getForeignKeyName(),
82-
$this->localRelationship->getLocalKeyName(),
83-
$distantRelation->getLocalKeyName(),
84-
);
92+
return $returnedRelation;
8593
}
8694

8795
/**
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Database\EloquentThroughTest;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Schema\Blueprint;
7+
use Illuminate\Support\Facades\Schema;
8+
use Illuminate\Tests\Integration\Database\DatabaseTestCase;
9+
10+
class EloquentThroughTest extends DatabaseTestCase
11+
{
12+
protected function afterRefreshingDatabase()
13+
{
14+
Schema::create('posts', function (Blueprint $table) {
15+
$table->increments('id');
16+
$table->boolean('public');
17+
});
18+
19+
Schema::create('other_commentables', function (Blueprint $table) {
20+
$table->increments('id');
21+
});
22+
23+
Schema::create('comments', function (Blueprint $table) {
24+
$table->increments('id');
25+
$table->string('commentable_type');
26+
$table->integer('commentable_id');
27+
});
28+
29+
Schema::create('likes', function (Blueprint $table) {
30+
$table->increments('id');
31+
$table->unsignedInteger('comment_id');
32+
});
33+
34+
35+
$post = tap(new Post(['public' => true]))->save();
36+
$comment = tap((new Comment)->commentable()->associate($post))->save();
37+
(new Like())->comment()->associate($comment)->save();
38+
(new Like())->comment()->associate($comment)->save();
39+
40+
$otherCommentable = tap(new OtherCommentable())->save();
41+
$comment2 = tap((new Comment)->commentable()->associate($otherCommentable))->save();
42+
(new Like())->comment()->associate($comment2)->save();
43+
}
44+
45+
public function test()
46+
{
47+
/** @var Post $post */
48+
$post = Post::first();
49+
$this->assertEquals(2, $post->commentLikes()->count());
50+
}
51+
}
52+
53+
class Comment extends Model
54+
{
55+
public $timestamps = false;
56+
57+
public function commentable()
58+
{
59+
return $this->morphTo();
60+
}
61+
62+
public function likes()
63+
{
64+
return $this->hasMany(Like::class);
65+
}
66+
}
67+
68+
class Post extends Model
69+
{
70+
public $timestamps = false;
71+
72+
protected $guarded = [];
73+
74+
protected $withCount = ['comments'];
75+
76+
public function comments()
77+
{
78+
return $this->morphMany(Comment::class, 'commentable');
79+
}
80+
81+
public function commentLikes()
82+
{
83+
return $this->through($this->comments())->has('likes');
84+
}
85+
86+
public function texts()
87+
{
88+
return $this->hasMany(Text::class);
89+
}
90+
}
91+
92+
class OtherCommentable extends Model
93+
{
94+
public $timestamps = false;
95+
96+
public function comments()
97+
{
98+
return $this->morphMany(Comment::class, 'commentable');
99+
}
100+
}
101+
102+
class Text extends Model
103+
{
104+
public $timestamps = false;
105+
106+
protected $guarded = [];
107+
108+
public function post()
109+
{
110+
return $this->belongsTo(Post::class);
111+
}
112+
}
113+
114+
class Like extends Model
115+
{
116+
public $timestamps = false;
117+
118+
public function comment()
119+
{
120+
return $this->belongsTo(Comment::class);
121+
}
122+
}

0 commit comments

Comments
 (0)