Skip to content

Commit b736cca

Browse files
committed
feat: correct builder parameter for other non-morph relational queries
1 parent 93f4b99 commit b736cca

File tree

3 files changed

+92
-7
lines changed

3 files changed

+92
-7
lines changed

src/Extensions/Eloquent/WhereHasBuilderType.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,26 @@ final class WhereHasBuilderType implements MethodParameterClosureTypeExtension
2828
{
2929
public function isMethodSupported(MethodReflection $method, ParameterReflection $parameter): bool
3030
{
31-
return $method->getName() === 'whereHas'
32-
&& $parameter->getName() === 'callback'
31+
$methods = [
32+
'doesntHave',
33+
'has',
34+
'whereHas',
35+
'withWhereHas',
36+
'orWhereHas',
37+
'whereDoesntHave',
38+
'orWhereDoesntHave',
39+
];
40+
41+
if (!in_array($method->getName(), $methods, true)) {
42+
return false;
43+
}
44+
45+
return $parameter->getName() === 'callback'
3346
&& $method->getDeclaringClass()->getName() === 'Illuminate\Database\Eloquent\Builder';
3447
}
3548

3649
public function getTypeFromMethodCall(MethodReflection $method, MethodCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type
3750
{
38-
if (count($methodCall->getArgs()) < 2) {
39-
return null;
40-
}
41-
4251
$firstType = $scope->getType($methodCall->getArgs()[0]->value);
4352

4453
$strings = $firstType->getConstantStrings();

stubs/database/eloquent/concerns/queries-relationships.stub

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,70 @@ use Closure;
99
*/
1010
trait QueriesRelationships
1111
{
12+
/**
13+
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
14+
* @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation
15+
* @param string $operator
16+
* @param int $count
17+
* @param string $boolean
18+
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
19+
* @return static
20+
* @throws \RuntimeException
21+
*/
22+
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null);
23+
24+
/**
25+
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
26+
* @param string $relation
27+
* @param string $boolean
28+
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
29+
* @return static
30+
*/
31+
public function doesntHave($relation, $boolean = 'and', ?Closure $callback = null);
32+
1233
/**
1334
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
1435
* @param string $relation
1536
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
1637
* @param string $operator
1738
* @param int $count
18-
* @return \Illuminate\Database\Eloquent\Builder<TModel>
39+
* @return static
1940
*/
2041
public function whereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1);
42+
43+
/**
44+
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
45+
* @param string $relation
46+
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
47+
* @param string $operator
48+
* @param int $count
49+
* @return static
50+
*/
51+
public function withWhereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1);
52+
53+
/**
54+
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
55+
* @param string $relation
56+
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
57+
* @param string $operator
58+
* @param int $count
59+
* @return static
60+
*/
61+
public function orWhereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1);
62+
63+
/**
64+
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
65+
* @param string $relation
66+
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
67+
* @return static
68+
*/
69+
public function whereDoesntHave($relation, ?Closure $callback = null);
70+
71+
/**
72+
* @template TBuilder of \Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>
73+
* @param string $relation
74+
* @param \Closure(TBuilder): (void|TBuilder)|null $callback
75+
* @return static
76+
*/
77+
public function orWhereDoesntHave($relation, ?Closure $callback = null);
2178
}

tests/Types/data/database/eloquent/builder.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@
7070
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->blocked(false));
7171

7272
// QueriesRelationships
73+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->has('posts', callback: function ($param1) {
74+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
75+
}));
76+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->doesntHave('posts', callback: function ($param1) {
77+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
78+
}));
7379
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->whereHas('posts', function ($param1) {
7480
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
7581
}));
@@ -94,3 +100,16 @@
94100
}));
95101

96102
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->whereHas($unionRelation));
103+
104+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->withWhereHas('posts', function ($param1) {
105+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
106+
}));
107+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->orWhereHas('posts', function ($param1) {
108+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
109+
}));
110+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->whereDoesntHave('posts', function ($param1) {
111+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
112+
}));
113+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\User>', $builder->orWhereDoesntHave('posts', function ($param1) {
114+
assertType('Illuminate\Database\Eloquent\Builder<Tests\Types\Fakes\Post>', $param1);
115+
}));

0 commit comments

Comments
 (0)