Skip to content

Commit ece8846

Browse files
Merge release 2.2.3 into 3.0.x (#214)
1 parent 5ceb925 commit ece8846

File tree

5 files changed

+57
-10
lines changed

5 files changed

+57
-10
lines changed

.github/workflows/release-on-milestone-closed.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
jobs:
99
release:
1010
name: "Git tag, release & create merge-up PR"
11-
runs-on: "ubuntu-20.04"
11+
runs-on: "ubuntu-latest"
1212

1313
steps:
1414
- name: "Checkout"

phpstan-baseline.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ parameters:
99
-
1010
message: '#^Doing instanceof PHPStan\\Type\\ObjectType is error\-prone and deprecated\. Use Type\:\:isObject\(\) or Type\:\:getObjectClassNames\(\) instead\.$#'
1111
identifier: phpstanApi.instanceofType
12-
count: 3
12+
count: 4
1313
path: src/Rules/NoDynamicWhereRule.php
1414

1515
-

src/Rules/NoDynamicWhereRule.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
88
use Illuminate\Database\Eloquent\Model;
99
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
10+
use Illuminate\Database\Eloquent\Relations\Relation;
1011
use Illuminate\Database\Query\Builder as QueryBuilder;
1112
use Larastan\Larastan\Methods\BuilderHelper;
1213
use PhpParser\Node;
@@ -76,7 +77,8 @@ public function processNode(Node $node, Scope $scope): array
7677
if (
7778
$calledOnType->isInstanceOf(Model::class)->no() &&
7879
$calledOnType->isInstanceOf(EloquentBuilder::class)->no() &&
79-
$calledOnType->isInstanceOf(QueryBuilder::class)->no()
80+
$calledOnType->isInstanceOf(QueryBuilder::class)->no() &&
81+
$calledOnType->isInstanceOf(Relation::class)->no()
8082
) {
8183
return [];
8284
}
@@ -130,13 +132,7 @@ public function processNode(Node $node, Scope $scope): array
130132

131133
private function getCalledOnType(MethodCall $node, Scope $scope): Type
132134
{
133-
$methodCall = $node;
134-
135-
while ($methodCall->var instanceof MethodCall) {
136-
$methodCall = $methodCall->var;
137-
}
138-
139-
$calledOnType = $scope->getType($methodCall->var);
135+
$calledOnType = $scope->getType($node->var);
140136

141137
if ($calledOnType instanceof ThisType) {
142138
$calledOnType = $calledOnType->getStaticObjectType();
@@ -164,6 +160,19 @@ private function findModel(ClassReflection $calledOnReflection): string|null
164160
return $modelType->getClassName();
165161
}
166162

163+
if (
164+
$calledOnReflection->getName() === Relation::class ||
165+
$calledOnReflection->isSubclassOf(Relation::class)
166+
) {
167+
$modelType = $calledOnReflection->getActiveTemplateTypeMap()->getType('TRelatedModel');
168+
169+
if (! $modelType instanceof ObjectType) {
170+
return null;
171+
}
172+
173+
return $modelType->getClassName();
174+
}
175+
167176
return null;
168177
}
169178
}

tests/Rules/NoDynamicWhereRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public function testRule(): void
4444
"Dynamic where method 'whereBaz' should not be used.",
4545
51,
4646
],
47+
[
48+
"Dynamic where method 'whereIsActive' should not be used.",
49+
152,
50+
],
4751
]);
4852
}
4953
}

tests/Rules/data/dynamic-where.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,41 +72,49 @@ public function whereBar(string $bar): CustomEloquentBuilder
7272

7373
class ModelWithPivotWhereClauses extends Model
7474
{
75+
/** @return BelongsToMany<Foo, $this> */
7576
public function fooWherePivot(): BelongsToMany
7677
{
7778
return $this->belongsToMany(Foo::class)->wherePivot('pivot_field', 1);
7879
}
7980

81+
/** @return BelongsToMany<Foo, $this> */
8082
public function fooWherePivotBetween(): BelongsToMany
8183
{
8284
return $this->belongsToMany(Foo::class)->wherePivotBetween('pivot_field', [1, 2]);
8385
}
8486

87+
/** @return BelongsToMany<Foo, $this> */
8588
public function fooWherePivotNotBetween(): BelongsToMany
8689
{
8790
return $this->belongsToMany(Foo::class)->wherePivotNotBetween('pivot_field', [1, 2]);
8891
}
8992

93+
/** @return BelongsToMany<Foo, $this> */
9094
public function fooWherePivotIn(): BelongsToMany
9195
{
9296
return $this->belongsToMany(Foo::class)->wherePivotIn('pivot_field', [1, 2]);
9397
}
9498

99+
/** @return BelongsToMany<Foo, $this> */
95100
public function fooWithPivotValue(): BelongsToMany
96101
{
97102
return $this->belongsToMany(Foo::class)->withPivotValue('pivot_field', 1);
98103
}
99104

105+
/** @return BelongsToMany<Foo, $this> */
100106
public function fooWherePivotNotIn(): BelongsToMany
101107
{
102108
return $this->belongsToMany(Foo::class)->wherePivotNotIn('pivot_field', [1, 2]);
103109
}
104110

111+
/** @return BelongsToMany<Foo, $this> */
105112
public function fooWherePivotNull(): BelongsToMany
106113
{
107114
return $this->belongsToMany(Foo::class)->wherePivotNull('pivot_field');
108115
}
109116

117+
/** @return BelongsToMany<Foo, $this> */
110118
public function fooWherePivotNotNull(): BelongsToMany
111119
{
112120
return $this->belongsToMany(Foo::class)->wherePivotNotNull('pivot_field');
@@ -129,3 +137,29 @@ public function scopeWhereFooBar($query): Builder
129137
return $query->where('foo', 'bar');
130138
}
131139
}
140+
141+
class Account extends Model
142+
{
143+
/** @return \Illuminate\Database\Eloquent\Relations\HasMany<AccountAction, $this> */
144+
public function actions(): \Illuminate\Database\Eloquent\Relations\HasMany
145+
{
146+
return $this->hasMany(AccountAction::class);
147+
}
148+
149+
public function hasActiveActions(): bool
150+
{
151+
if (rand(0, 100) > 40) {
152+
return $this->actions()->whereIsActive()->exists();
153+
}
154+
155+
return $this->actions()->whereActive()->exists();
156+
}
157+
}
158+
159+
class AccountAction extends Model
160+
{
161+
public function scopeWhereActive(Builder $query): Builder
162+
{
163+
return $query->where('is_active', true);
164+
}
165+
}

0 commit comments

Comments
 (0)