Skip to content

Commit e20600b

Browse files
lopesrbrunoBruno Lopes
andauthored
fix morph to many (#137)
Co-authored-by: Bruno Lopes <[email protected]>
1 parent 7b4c6b7 commit e20600b

File tree

7 files changed

+166
-3
lines changed

7 files changed

+166
-3
lines changed

src/Mixins/RelationshipsExtraMethods.php

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
1313
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
1414
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
15+
use Illuminate\Database\Eloquent\Relations\MorphToMany;
1516

1617
/**
1718
* @method getModel
@@ -25,10 +26,12 @@ class RelationshipsExtraMethods
2526
public function performJoinForEloquentPowerJoins()
2627
{
2728
return function ($builder, $joinType = 'leftJoin', $callback = null, $alias = null, bool $disableExtraConditions = false) {
28-
if ($this instanceof BelongsToMany) {
29+
if ($this instanceof MorphToMany) {
30+
return $this->performJoinForEloquentPowerJoinsForMorphToMany($builder, $joinType, $callback, $alias, $disableExtraConditions);
31+
} elseif ($this instanceof BelongsToMany) {
2932
return $this->performJoinForEloquentPowerJoinsForBelongsToMany($builder, $joinType, $callback, $alias, $disableExtraConditions);
3033
} elseif ($this instanceof MorphOneOrMany) {
31-
$this->performJoinForEloquentPowerJoinsForMorph($builder, $joinType, $callback, $alias, $disableExtraConditions);
34+
return $this->performJoinForEloquentPowerJoinsForMorph($builder, $joinType, $callback, $alias, $disableExtraConditions);
3235
} elseif ($this instanceof HasMany || $this instanceof HasOne) {
3336
return $this->performJoinForEloquentPowerJoinsForHasMany($builder, $joinType, $callback, $alias, $disableExtraConditions);
3437
} elseif ($this instanceof HasManyThrough) {
@@ -130,6 +133,62 @@ protected function performJoinForEloquentPowerJoinsForBelongsToMany()
130133
};
131134
}
132135

136+
/**
137+
* Perform the JOIN clause for the MorphToMany (or similar) relationships.
138+
*/
139+
protected function performJoinForEloquentPowerJoinsForMorphToMany()
140+
{
141+
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
142+
[$alias1, $alias2] = $alias;
143+
144+
$joinedTable = $alias1 ?: $this->getTable();
145+
$parentTable = $this->getTableOrAliasForModel($this->parent) ?? $this->parent->getTable();
146+
147+
$builder->{$joinType}($this->getTable(), function ($join) use ($callback, $joinedTable, $parentTable, $alias1, $disableExtraConditions) {
148+
if ($alias1) {
149+
$join->as($alias1);
150+
}
151+
152+
$join->on(
153+
"{$joinedTable}.{$this->getForeignPivotKeyName()}",
154+
'=',
155+
"{$parentTable}.{$this->parentKey}"
156+
);
157+
158+
// applying any extra conditions to the belongs to many relationship
159+
if ($disableExtraConditions === false) {
160+
$this->applyExtraConditions($join);
161+
}
162+
163+
if (is_array($callback) && isset($callback[$this->getTable()])) {
164+
$callback[$this->getTable()]($join);
165+
}
166+
});
167+
168+
$builder->{$joinType}($this->getModel()->getTable(), function ($join) use ($callback, $joinedTable, $alias2, $disableExtraConditions) {
169+
if ($alias2) {
170+
$join->as($alias2);
171+
}
172+
173+
$join->on(
174+
"{$this->getModel()->getTable()}.{$this->getModel()->getKeyName()}",
175+
'=',
176+
"{$joinedTable}.{$this->getRelatedPivotKeyName()}"
177+
);
178+
179+
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
180+
$join->whereNull($this->query->getModel()->getQualifiedDeletedAtColumn());
181+
}
182+
183+
if (is_array($callback) && isset($callback[$this->getModel()->getTable()])) {
184+
$callback[$this->getModel()->getTable()]($join);
185+
}
186+
}, $this->getModel());
187+
188+
return $this;
189+
};
190+
}
191+
133192
/**
134193
* Perform the JOIN clause for the Morph (or similar) relationships.
135194
*/
@@ -316,7 +375,7 @@ public function applyExtraConditions()
316375
continue;
317376
}
318377

319-
if (! in_array($condition['type'], ['Basic', 'Null', 'NotNull', 'Nested'])) {
378+
if (!in_array($condition['type'], ['Basic', 'Null', 'NotNull', 'Nested'])) {
320379
continue;
321380
}
322381

tests/JoinRelationshipExtraConditionsTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Kirschbaum\PowerJoins\Tests\Models\Post;
66
use Kirschbaum\PowerJoins\Tests\Models\User;
7+
use Kirschbaum\PowerJoins\Tests\Models\Tag;
78
use Kirschbaum\PowerJoins\Tests\Models\Group;
89
use Kirschbaum\PowerJoins\Tests\Models\Image;
910
use Kirschbaum\PowerJoins\Tests\Models\Comment;
@@ -184,6 +185,52 @@ public function test_extra_conditions_in_morph_many()
184185
);
185186
}
186187

188+
/** @test */
189+
public function test_extra_conditions_in_morph_to_many()
190+
{
191+
$tag = factory(Tag::class)->create();
192+
$post = factory(Post::class)->create();
193+
$comment = factory(Comment::class)->create();
194+
195+
$tag->posts()->attach($post->id);
196+
$tag->comments()->attach($comment->id);
197+
198+
$postsQuery = Post::joinRelationship('tags');
199+
$commentsQuery = Comment::joinRelationship('tags');
200+
201+
$this->assertCount(1, $postsQuery->get());
202+
$this->assertCount(1, $commentsQuery->get());
203+
204+
$this->assertStringContainsString(
205+
'inner join "taggables" on "taggables"."taggable_id" = "posts"."id" and "taggables"."taggable_type" = ?',
206+
$postsQuery->toSql()
207+
);
208+
209+
$this->assertStringContainsString(
210+
'inner join "taggables" on "taggables"."taggable_id" = "comments"."id" and "taggables"."taggable_type" = ?',
211+
$commentsQuery->toSql()
212+
);
213+
}
214+
215+
/** @test */
216+
public function test_count_in_morph_to_many_left_join()
217+
{
218+
$tag = factory(Tag::class)->create();
219+
$post = factory(Post::class)->create();
220+
$comment = factory(Comment::class)->create([
221+
'post_id' => $post->id,
222+
]);
223+
224+
$tag->posts()->attach($post->id);
225+
$tag->comments()->attach($comment->id);
226+
227+
$posts = Post::leftJoinRelationship('tags')->get();
228+
$comments = Comment::leftJoinRelationship('tags')->get();
229+
230+
$this->assertCount(1, $posts);
231+
$this->assertCount(1, $comments);
232+
}
233+
187234
/** @test */
188235
public function test_extra_conditions_with_closure()
189236
{

tests/Models/Comment.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Database\Eloquent\Model;
66
use Kirschbaum\PowerJoins\PowerJoins;
77
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8+
use Illuminate\Database\Eloquent\Relations\MorphToMany;
89

910
class Comment extends Model
1011
{
@@ -22,4 +23,9 @@ public function post(): BelongsTo
2223
{
2324
return $this->belongsTo(Post::class);
2425
}
26+
27+
public function tags(): MorphToMany
28+
{
29+
return $this->morphToMany(Tag::class, 'taggable');
30+
}
2531
}

tests/Models/Post.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Database\Eloquent\Relations\HasMany;
88
use Illuminate\Database\Eloquent\Relations\BelongsTo;
99
use Illuminate\Database\Eloquent\Relations\MorphMany;
10+
use Illuminate\Database\Eloquent\Relations\MorphToMany;
1011

1112
class Post extends Model
1213
{
@@ -45,6 +46,11 @@ public function coverImages(): MorphMany
4546
return $this->morphMany(Image::class, 'imageable')->where('cover', true);
4647
}
4748

49+
public function tags(): MorphToMany
50+
{
51+
return $this->morphToMany(Tag::class, 'taggable');
52+
}
53+
4854
public function category(): BelongsTo
4955
{
5056
return $this->belongsTo(Category::class);

tests/Models/Tag.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Kirschbaum\PowerJoins\Tests\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Eloquent\Relations\MorphToMany;
7+
use Kirschbaum\PowerJoins\PowerJoins;
8+
9+
class Tag extends Model
10+
{
11+
use PowerJoins;
12+
13+
/** @var string */
14+
protected $table = 'tags';
15+
16+
public function posts(): MorphToMany
17+
{
18+
return $this->morphedByMany(Post::class, 'taggable');
19+
}
20+
21+
public function comments(): MorphToMany
22+
{
23+
return $this->morphedByMany(Comment::class, 'taggable');
24+
}
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
use Kirschbaum\PowerJoins\Tests\Models\Tag;
4+
5+
$factory->define(Tag::class, function (Faker\Generator $faker) {
6+
return [
7+
'name' => $faker->words(3, true),
8+
];
9+
});

tests/database/migrations/2020_03_16_000000_create_tables.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ public function up()
8484
$table->boolean('cover')->default(false);
8585
$table->timestamps();
8686
});
87+
88+
Schema::create('tags', function (Blueprint $table) {
89+
$table->increments('id');
90+
$table->string('name');
91+
$table->timestamps();
92+
});
93+
94+
Schema::create('taggables', function (Blueprint $table) {
95+
$table->foreignId('tag_id');
96+
$table->morphs('taggable');
97+
});
8798
}
8899

89100
/**

0 commit comments

Comments
 (0)