Skip to content

Commit 260f8d2

Browse files
authored
Added ability to use array callbacks in nested powerJoinWhereHas (#163)
1 parent 52a1282 commit 260f8d2

File tree

3 files changed

+24
-42
lines changed

3 files changed

+24
-42
lines changed

README.md

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -100,21 +100,15 @@ Note: Querying morph to relationships only supports one morphable type at a time
100100
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.
101101

102102
```php
103-
User::joinRelationship('posts', function ($join) {
104-
$join->where('posts.approved', true);
105-
})->toSql();
103+
User::joinRelationship('posts', fn ($join) => $join->where('posts.approved', true))->toSql();
106104
```
107105

108106
For **nested calls**, you simply need to pass an array referencing the relationship names.
109107

110108
```php
111109
User::joinRelationship('posts.comments', [
112-
'posts' => function ($join) {
113-
$join->where('posts.published', true);
114-
},
115-
'comments' => function ($join) {
116-
$join->where('comments.approved', true);
117-
}
110+
'posts' => fn ($join) => $join->where('posts.published', true),
111+
'comments' => fn ($join) => $join->where('comments.approved', true),
118112
]);
119113
```
120114

@@ -127,9 +121,7 @@ User::joinRelationship('groups', [
127121
// ...
128122
},
129123
// group_members is the intermediary table here
130-
'group_members' => function ($join) {
131-
$join->where('group_members.active', true);
132-
},
124+
'group_members' => fn ($join) => $join->where('group_members.active', true),
133125
]
134126
]);
135127
```
@@ -176,12 +168,8 @@ Post::joinRelationshipUsingAlias('category', 'category_alias')->get();
176168

177169
```php
178170
Post::joinRelationship('category.parent', [
179-
'category' => function ($join) {
180-
$join->as('category_alias');
181-
},
182-
'parent' => function ($join) {
183-
$join->as('category_parent');
184-
},
171+
'category' => fn ($join) => $join->as('category_alias'),
172+
'parent' => fn ($join) => $join->as('category_parent'),
185173
])->get()
186174
```
187175

@@ -190,12 +178,8 @@ For *belongs to many* or *has many through* calls, you need to pass an array wit
190178
```php
191179
Group::joinRelationship('posts.user', [
192180
'posts' => [
193-
'posts' => function ($join) {
194-
$join->as('posts_alias');
195-
},
196-
'post_groups' => function($join) {
197-
$join->as('post_groups_alias');
198-
},
181+
'posts' => fn ($join) => $join->as('posts_alias'),
182+
'post_groups' => fn ($join) => $join->as('post_groups_alias'),
199183
],
200184
])->toSql();
201185
```
@@ -227,17 +211,13 @@ and "users"."deleted_at" is null
227211
In case you want to include trashed models, you can call the `->withTrashed()` method in the join callback.
228212

229213
```php
230-
UserProfile::joinRelationship('users', function ($join) {
231-
$join->withTrashed();
232-
});
214+
UserProfile::joinRelationship('users', fn ($join) => $join->withTrashed());
233215
```
234216

235217
You can also call the `onlyTrashed` model as well:
236218

237219
```php
238-
UserProfile::joinRelationship('users', function ($join) {
239-
$join->onlyTrashed();
240-
});
220+
UserProfile::joinRelationship('users', ($join) => $join->onlyTrashed());
241221
```
242222

243223
#### Extra conditions defined in relationships
@@ -265,9 +245,7 @@ select users.* from users inner join posts on posts.user_id = posts.id and posts
265245
If your model have global scopes applied to it, you can enable the global scopes by calling the `withGlobalScopes` method in your join clause, like this:
266246

267247
```php
268-
UserProfile::joinRelationship('users', function ($join) {
269-
$join->withGlobalScopes();
270-
});
248+
UserProfile::joinRelationship('users', fn ($join) => $join->withGlobalScopes());
271249
```
272250

273251
There's, though, a gotcha here. Your global scope **cannot** type-hint the `Eloquent\Builder` class in the first parameter of the `apply` method, otherwise you will get errors.
@@ -286,9 +264,8 @@ Please note that although the methods are similar, you will not always get the s
286264
User::has('posts');
287265
User::has('posts.comments');
288266
User::has('posts', '>', 3);
289-
User::whereHas('posts', function ($query) {
290-
$query->where('posts.published', true);
291-
});
267+
User::whereHas('posts', fn ($query) => $query->where('posts.published', true));
268+
User::whereHas('posts.comments', ['posts' => fn ($query) => $query->where('posts.published', true));
292269
User::doesntHave('posts');
293270
```
294271

src/Mixins/JoinRelationship.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ public function orderByLeftPowerJoinsMax(): Closure
461461
*/
462462
public function powerJoinHas(): Closure
463463
{
464-
return function ($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null, string $morphable = null): static {
464+
return function ($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure|array $callback = null, string $morphable = null): static {
465465
if (is_null($this->getSelect())) {
466466
$this->select(sprintf('%s.*', $this->getModel()->getTable()));
467467
}
@@ -487,20 +487,22 @@ public function powerJoinHas(): Closure
487487

488488
public function hasNestedUsingJoins(): Closure
489489
{
490-
return function ($relations, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null): static {
490+
return function ($relations, $operator = '>=', $count = 1, $boolean = 'and', Closure|array $callback = null): static {
491491
$relations = explode('.', $relations);
492492

493493
/** @var Relation */
494494
$latestRelation = null;
495495

496496
foreach ($relations as $index => $relation) {
497-
if (!$latestRelation) {
497+
$relationName = $relation;
498+
499+
if (! $latestRelation) {
498500
$relation = $this->getRelationWithoutConstraintsProxy($relation);
499501
} else {
500502
$relation = $latestRelation->getModel()->query()->getRelationWithoutConstraintsProxy($relation);
501503
}
502504

503-
$relation->performJoinForEloquentPowerJoins($this, 'leftPowerJoin', $callback);
505+
$relation->performJoinForEloquentPowerJoins($this, 'leftPowerJoin', is_callable($callback) ? $callback : $callback[$relationName] ?? null);
504506

505507
if (count($relations) === ($index + 1)) {
506508
$relation->performHavingForEloquentPowerJoins($this, $operator, $count);

tests/PowerJoinHasTest.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,16 @@ public function test_where_has_using_joins()
104104
public function test_has_with_joins_and_nested_relations()
105105
{
106106
[$user1, $user2] = factory(User::class)->times(2)->create();
107-
$post1 = factory(Post::class)->create(['user_id' => $user1->id]);
108-
$post2 = factory(Post::class)->create(['user_id' => $user2->id]);
107+
$post1 = factory(Post::class)->state('published')->create(['user_id' => $user1->id]);
108+
$post2 = factory(Post::class)->state('unpublished')->create(['user_id' => $user2->id]);
109109
$commentsPost1 = factory(Comment::class)->times(2)->create(['post_id' => $post1->id]);
110110
$commentsPost2 = factory(Comment::class)->times(5)->create(['post_id' => $post2->id]);
111111

112112
$this->assertCount(2, User::has('posts.comments')->get());
113113
$this->assertCount(2, User::powerJoinHas('posts.comments')->get());
114+
$this->assertCount(1, User::powerJoinWhereHas('posts.comments', callback: [
115+
'posts' => fn ($query) => $query->where('posts.published', true)
116+
])->get());
114117
}
115118

116119
/** @test */

0 commit comments

Comments
 (0)