Skip to content

Commit 9dd336b

Browse files
authored
Added support for morphTo (#158)
1 parent d1054e6 commit 9dd336b

File tree

7 files changed

+98
-18
lines changed

7 files changed

+98
-18
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ Turns out, if you join a polymorphic relationship, Eloquent Power Joins automati
8787
Post::joinRelationship('images');
8888
```
8989

90+
You can also join MorphTo relationships.
91+
92+
```php
93+
Image::joinRelationship('imageable', morphable: Post::class);
94+
```
95+
96+
Note: Querying morph to relationships only supports one morphable type at a time.
97+
9098
**Applying conditions & callbacks to the joins**
9199

92100
Now, let's say you want to apply a condition to the join you are making. You simply need to pass a callback as the second parameter to the `joinRelationship` method.

src/Mixins/JoinRelationship.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ public function joinRelationship(): Closure
9393
$callback = null,
9494
$joinType = 'join',
9595
$useAlias = false,
96-
bool $disableExtraConditions = false
96+
bool $disableExtraConditions = false,
97+
string $morphable = null
9798
) {
9899
$joinType = JoinsHelper::$joinMethodsMap[$joinType] ?? $joinType;
99100
$useAlias = is_string($callback) ? false : $useAlias;
@@ -145,7 +146,8 @@ public function joinRelationship(): Closure
145146
joinType: $joinType,
146147
callback: $callback,
147148
alias: $alias,
148-
disableExtraConditions: $disableExtraConditions
149+
disableExtraConditions: $disableExtraConditions,
150+
morphable: $morphable,
149151
);
150152

151153
return $this;

src/Mixins/RelationshipsExtraMethods.php

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44

55
use Kirschbaum\PowerJoins\StaticCache;
66
use Kirschbaum\PowerJoins\PowerJoinClause;
7+
use Kirschbaum\PowerJoins\Tests\Models\Post;
78
use Illuminate\Database\Eloquent\SoftDeletes;
89
use Illuminate\Database\Eloquent\Relations\HasOne;
910
use Illuminate\Database\Eloquent\Relations\HasMany;
11+
use Illuminate\Database\Eloquent\Relations\MorphTo;
1012
use Illuminate\Database\Eloquent\Relations\BelongsTo;
13+
use Illuminate\Database\Eloquent\Relations\MorphToMany;
1114
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
1215
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
1316
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
14-
use Illuminate\Database\Eloquent\Relations\MorphToMany;
1517

1618
/**
1719
* @method \Illuminate\Database\Eloquent\Model getModel()
@@ -49,20 +51,16 @@ class RelationshipsExtraMethods
4951
*/
5052
public function performJoinForEloquentPowerJoins()
5153
{
52-
return function ($builder, $joinType = 'leftJoin', $callback = null, $alias = null, bool $disableExtraConditions = false) {
53-
if ($this instanceof MorphToMany) {
54-
return $this->performJoinForEloquentPowerJoinsForMorphToMany($builder, $joinType, $callback, $alias, $disableExtraConditions);
55-
} elseif ($this instanceof BelongsToMany) {
56-
return $this->performJoinForEloquentPowerJoinsForBelongsToMany($builder, $joinType, $callback, $alias, $disableExtraConditions);
57-
} elseif ($this instanceof MorphOneOrMany) {
58-
return $this->performJoinForEloquentPowerJoinsForMorph($builder, $joinType, $callback, $alias, $disableExtraConditions);
59-
} elseif ($this instanceof HasMany || $this instanceof HasOne) {
60-
return $this->performJoinForEloquentPowerJoinsForHasMany($builder, $joinType, $callback, $alias, $disableExtraConditions);
61-
} elseif ($this instanceof HasManyThrough) {
62-
return $this->performJoinForEloquentPowerJoinsForHasManyThrough($builder, $joinType, $callback, $alias, $disableExtraConditions);
63-
} else {
64-
return $this->performJoinForEloquentPowerJoinsForBelongsTo($builder, $joinType, $callback, $alias, $disableExtraConditions);
65-
}
54+
return function ($builder, $joinType = 'leftJoin', $callback = null, $alias = null, bool $disableExtraConditions = false, string $morphable = null) {
55+
return match (true) {
56+
$this instanceof MorphToMany => $this->performJoinForEloquentPowerJoinsForMorphToMany($builder, $joinType, $callback, $alias, $disableExtraConditions),
57+
$this instanceof BelongsToMany => $this->performJoinForEloquentPowerJoinsForBelongsToMany($builder, $joinType, $callback, $alias, $disableExtraConditions),
58+
$this instanceof MorphOneOrMany => $this->performJoinForEloquentPowerJoinsForMorph($builder, $joinType, $callback, $alias, $disableExtraConditions),
59+
$this instanceof HasMany || $this instanceof HasOne => $this->performJoinForEloquentPowerJoinsForHasMany($builder, $joinType, $callback, $alias, $disableExtraConditions),
60+
$this instanceof HasManyThrough => $this->performJoinForEloquentPowerJoinsForHasManyThrough($builder, $joinType, $callback, $alias, $disableExtraConditions),
61+
$this instanceof MorphTo => $this->performJoinForEloquentPowerJoinsForMorphTo($builder, $joinType, $callback, $alias, $disableExtraConditions, $morphable),
62+
default => $this->performJoinForEloquentPowerJoinsForBelongsTo($builder, $joinType, $callback, $alias, $disableExtraConditions),
63+
};
6664
};
6765
}
6866

@@ -243,6 +241,38 @@ protected function performJoinForEloquentPowerJoinsForMorph()
243241
};
244242
}
245243

244+
/**
245+
* Perform the JOIN clause for when calling the morphTo method from the morphable class.
246+
*/
247+
protected function performJoinForEloquentPowerJoinsForMorphTo()
248+
{
249+
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false, string $morphable = null) {
250+
$modelInstance = new $morphable;
251+
252+
$builder->{$joinType}($modelInstance->getTable(), function ($join) use ($modelInstance, $callback, $disableExtraConditions) {
253+
$join->on(
254+
"{$this->getModel()->getTable()}.{$this->getForeignKeyName()}",
255+
'=',
256+
"{$modelInstance->getTable()}.{$modelInstance->getKeyName()}"
257+
)->where("{$this->getModel()->getTable()}.{$this->getMorphType()}", '=', $modelInstance->getMorphClass());
258+
259+
if ($disableExtraConditions === false && $this->usesSoftDeletes($this->query->getModel())) {
260+
$join->whereNull($this->query->getModel()->getQualifiedDeletedAtColumn());
261+
}
262+
263+
if ($disableExtraConditions === false) {
264+
$this->applyExtraConditions($join);
265+
}
266+
267+
if ($callback && is_callable($callback)) {
268+
$callback($join);
269+
}
270+
}, $this->getModel());
271+
272+
return $this;
273+
};
274+
}
275+
246276
/**
247277
* Perform the JOIN clause for the HasMany (or similar) relationships.
248278
*/

src/PowerJoinClause.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ public function on($first, $operator = null, $second = null, $boolean = 'and'):
7676
return $this;
7777
}
7878

79+
public function morphableClass(string $modelClass)
80+
{
81+
dd($modelClass);
82+
}
83+
7984
public function getModel()
8085
{
8186
return $this->model;

tests/JoinRelationshipTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,4 +795,25 @@ public function test_it_doesnt_fail_to_join_the_same_query_repeatedly()
795795
}
796796
}
797797
}
798+
799+
public function test_join_morph_to_morphable_class()
800+
{
801+
factory(Image::class, 5)->state('owner:post')->create();
802+
factory(Image::class, 4)->state('owner:user')->create();
803+
804+
$postImages = Image::query()
805+
->joinRelationship('imageable', morphable: Post::class)
806+
->get();
807+
808+
$sql = Image::query()
809+
->joinRelationship('imageable', morphable: Post::class)
810+
->toSql();
811+
812+
$this->assertStringContainsString(
813+
'inner join "posts" on "images"."imageable_id" = "posts"."id" and "images"."imageable_type" = ?',
814+
$sql
815+
);
816+
817+
$this->assertCount(5, $postImages);
818+
}
798819
}

tests/Models/User.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace Kirschbaum\PowerJoins\Tests\Models;
44

5+
use Kirschbaum\PowerJoins\PowerJoins;
56
use Illuminate\Database\Eloquent\Model;
67
use Illuminate\Database\Eloquent\SoftDeletes;
7-
use Kirschbaum\PowerJoins\PowerJoins;
88
use Illuminate\Database\Eloquent\Relations\HasOne;
99
use Illuminate\Database\Eloquent\Relations\HasMany;
10+
use Illuminate\Database\Eloquent\Relations\MorphMany;
1011
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
1112
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
1213

@@ -64,6 +65,11 @@ public function commentsThroughPosts(): HasManyThrough
6465
return $this->hasManyThrough(Comment::class, Post::class);
6566
}
6667

68+
public function images(): MorphMany
69+
{
70+
return $this->morphMany(Image::class, 'imageable');
71+
}
72+
6773
public function scopeHasPublishedPosts($query)
6874
{
6975
$query->powerJoinWhereHas('posts', function ($join) {

tests/database/factories/ImageFactory.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use Carbon\Carbon;
44
use Kirschbaum\PowerJoins\Tests\Models\Post;
5+
use Kirschbaum\PowerJoins\Tests\Models\User;
56
use Kirschbaum\PowerJoins\Tests\Models\Image;
67

78
$factory->define(Image::class, function (Faker\Generator $faker) {
@@ -17,6 +18,13 @@
1718
},
1819
]);
1920

21+
$factory->state(Image::class, 'owner:user', [
22+
'imageable_type' => User::class,
23+
'imageable_id' => function () {
24+
return factory(User::class)->create()->id;
25+
},
26+
]);
27+
2028
$factory->state(Image::class, 'cover', [
2129
'cover' => 1,
2230
]);

0 commit comments

Comments
 (0)