Skip to content
This repository was archived by the owner on Nov 21, 2023. It is now read-only.

Commit 7345abd

Browse files
authored
Merge pull request #12 from mpyw/feature/support-union-argument
Revise: "Add support for union argument in constraint callable #11"
2 parents 26ac295 + 2c17a0b commit 7345abd

File tree

4 files changed

+148
-3
lines changed

4 files changed

+148
-3
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ Additional `callable` constraints for relations that take **`Illuminate\Database
120120
Builder::hasByNonDependentSubquery('comments', fn (HasMany $query) => $query->withTrashed())
121121
```
122122

123+
If you are using a union type as of PHP 8.0, the order of types does not matter.
124+
125+
```php
126+
// This will work
127+
Builder::hasByNonDependentSubquery('comments', fn (HasMany|Comment $query) => $query->withTrashed())
128+
// and so will this
129+
Builder::hasByNonDependentSubquery('comments', fn (Comment|HasMany $query) => $query->withTrashed())
130+
```
131+
123132
The first closure corresponds to `comments` and the second one corresponds to `author`.
124133

125134
```php

src/HasByNonDependentSubqueryMacro.php

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use DomainException;
66
use Illuminate\Database\Eloquent\Builder;
77
use Illuminate\Database\Eloquent\Relations\Relation;
8+
use ReflectionNamedType;
9+
use ReflectionType;
810

911
/**
1012
* Class HasByNonDependentSubqueryMacro
@@ -148,8 +150,49 @@ protected function adjustArgumentTypeOfOptionalConstraints(callable $constraint,
148150

149151
return $reflection->getNumberOfParameters() > 0
150152
&& ($parameter = $reflection->getParameters()[0])->hasType()
151-
&& \is_a($parameter->getType()->getName(), Builder::class, true)
152-
? $relation->getQuery()
153-
: $relation;
153+
&& $this->mustExtractEloquentBuilder($parameter->getType())
154+
? $relation->getQuery()
155+
: $relation;
156+
}
157+
158+
/**
159+
* @param \ReflectionNamedType|\ReflectionType|\ReflectionUnionType $type
160+
* @return bool
161+
*/
162+
protected function mustExtractEloquentBuilder(ReflectionType $type): bool
163+
{
164+
/* @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */
165+
return $type instanceof \ReflectionUnionType
166+
? $this->onlyIncludesBuilderType($type)
167+
: $this->namedTypeIs($type, Builder::class);
168+
}
169+
170+
/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */
171+
172+
/**
173+
* @param \ReflectionUnionType $types
174+
* @return bool
175+
*/
176+
protected function onlyIncludesBuilderType(\ReflectionUnionType $types): bool
177+
{
178+
$includesRelationType = false;
179+
$includesBuilderType = false;
180+
181+
foreach ($types->getTypes() as $type) {
182+
$includesRelationType = $includesRelationType || $this->namedTypeIs($type, Relation::class);
183+
$includesBuilderType = $includesBuilderType || $this->namedTypeIs($type, Builder::class);
184+
}
185+
186+
return !$includesRelationType && $includesBuilderType;
187+
}
188+
189+
/**
190+
* @param \ReflectionNamedType $type
191+
* @param string $class
192+
* @return bool
193+
*/
194+
protected function namedTypeIs(ReflectionNamedType $type, string $class): bool
195+
{
196+
return \is_a($type->getName(), $class, true);
154197
}
155198
}

tests/Test.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,15 @@ function (Builder $query) {
724724
);
725725
}
726726

727+
public function testAcceptUnionArgument(): void
728+
{
729+
if (version_compare(phpversion(), '8.0', '>=')) {
730+
include __DIR__ . '/includes/test_accept_union_argument.php';
731+
} else {
732+
$this->markTestSkipped('Union types are only supported in PHP >= 8.0');
733+
}
734+
}
735+
727736
public function testMultiColumnRelation(): void
728737
{
729738
$this->expectException(DomainException::class);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
use Illuminate\Database\Eloquent\Builder;
4+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
5+
use Mpyw\EloquentHasByNonDependentSubquery\Tests\Models\Comment;
6+
use Mpyw\EloquentHasByNonDependentSubquery\Tests\Models\Post;
7+
8+
$this->assertQueryEquals(
9+
<<<'EOD'
10+
select
11+
*
12+
from
13+
"comments"
14+
where
15+
"comments"."post_id" in (
16+
select
17+
"posts"."id"
18+
from
19+
"posts"
20+
where
21+
"posts"."deleted_at" is not null
22+
)
23+
EOD
24+
,
25+
Comment::query()->hasByNonDependentSubquery(
26+
'post',
27+
function (Post|BelongsTo $query) {
28+
$this->assertInstanceof(BelongsTo::class, $query);
29+
$query->onlyTrashed();
30+
}
31+
)->withTrashed()
32+
);
33+
34+
$this->assertQueryEquals(
35+
<<<'EOD'
36+
select
37+
*
38+
from
39+
"comments"
40+
where
41+
"comments"."post_id" in (
42+
select
43+
"posts"."id"
44+
from
45+
"posts"
46+
where
47+
"posts"."deleted_at" is not null
48+
)
49+
EOD
50+
,
51+
Comment::query()->hasByNonDependentSubquery(
52+
'post',
53+
function (BelongsTo|Builder $query) {
54+
$this->assertInstanceof(BelongsTo::class, $query);
55+
$query->onlyTrashed();
56+
}
57+
)->withTrashed()
58+
);
59+
60+
$this->assertQueryEquals(
61+
<<<'EOD'
62+
select
63+
*
64+
from
65+
"comments"
66+
where
67+
"comments"."post_id" in (
68+
select
69+
"posts"."id"
70+
from
71+
"posts"
72+
where
73+
"posts"."deleted_at" is not null
74+
)
75+
EOD
76+
,
77+
Comment::query()->hasByNonDependentSubquery(
78+
'post',
79+
function (Post|Builder $query) {
80+
$this->assertInstanceof(Builder::class, $query);
81+
$query->onlyTrashed();
82+
}
83+
)->withTrashed()
84+
);

0 commit comments

Comments
 (0)