Skip to content

Commit b50bde0

Browse files
authored
Merge pull request #77 from worksome/bugfix/without-timestamps
fix: add support for builder types
2 parents bc4f5fa + 84afb19 commit b50bde0

File tree

7 files changed

+53
-26
lines changed

7 files changed

+53
-26
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"require-dev": {
2020
"composer/composer": "^2.7",
2121
"friendsofphp/php-cs-fixer": "^3.53",
22+
"orchestra/testbench": "^9.5",
2223
"pestphp/pest": "^2.34",
2324
"spatie/invade": "^1.1.1",
2425
"squizlabs/php_codesniffer": "^3.8"

src/PHPStan/Laravel/Migrations/WithoutTimestampsVisitor.php

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Worksome\CodingStyle\PHPStan\Laravel\Migrations;
66

7+
use Illuminate\Database\Eloquent\Builder;
78
use Illuminate\Database\Eloquent\Model;
89
use PhpParser\Node;
910
use PhpParser\Node\Expr\MethodCall;
@@ -107,27 +108,40 @@ private function isModelCall(Node $node): bool
107108
return false;
108109
}
109110

110-
if ($node instanceof MethodCall) {
111-
$varType = $this->scope->getType($node->var);
111+
if ($node instanceof StaticCall) {
112+
if (! $node->class instanceof Node\Name) {
113+
return false;
114+
}
115+
116+
$className = $this->scope->resolveName($node->class);
117+
118+
if (! $className) {
119+
return false;
120+
}
112121

113122
$modelType = new ObjectType(Model::class);
123+
$classType = new ObjectType($className);
114124

115-
return $varType->isSuperTypeOf($modelType)->yes();
125+
return $modelType->isSuperTypeOf($classType)->yes();
116126
}
117127

118-
if (! $node instanceof StaticCall || ! $node->class instanceof Node\Name) {
119-
return false;
120-
}
128+
$classType = $this->scope->getType($node->var);
121129

122-
$className = $this->scope->resolveName($node->class);
130+
$modelType = new ObjectType(Model::class);
131+
$builderType = new ObjectType(Builder::class);
132+
133+
if ($modelType->isSuperTypeOf($classType)->yes()) {
134+
return true;
135+
}
123136

124-
if (! $className) {
137+
if (! $builderType->isSuperTypeOf($classType)->yes()) {
125138
return false;
126139
}
127140

128-
$modelType = new ObjectType(Model::class);
129-
$classType = new ObjectType($className);
141+
if (! isset($classType->getTypes()[0])) {
142+
return false;
143+
}
130144

131-
return $classType->isSubclassOf($modelType)->yes();
145+
return $modelType->isSuperTypeOf($classType->getTypes()[0])->yes();
132146
}
133147
}

tests/PHPStan/BaseRuleTestCase.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,34 @@
22

33
namespace Worksome\CodingStyle\Tests\PHPStan;
44

5+
use Larastan\Larastan\ApplicationResolver;
56
use PHPStan\Rules\Rule;
67
use PHPStan\Testing\RuleTestCase;
78

89
class BaseRuleTestCase extends RuleTestCase
910
{
1011
public Rule $rule;
1112

13+
protected function setUp(): void
14+
{
15+
parent::setUp();
16+
17+
if (! defined('LARAVEL_VERSION') && class_exists(ApplicationResolver::class)) {
18+
define('LARAVEL_VERSION', ApplicationResolver::resolve()->version());
19+
}
20+
}
21+
1222
protected function getRule(): Rule
1323
{
1424
return $this->rule;
1525
}
1626

1727
public static function getAdditionalConfigFiles(): array
1828
{
19-
return array_merge(parent::getAdditionalConfigFiles(), [
29+
return [
30+
... parent::getAdditionalConfigFiles(),
2031
__DIR__ . '/../../phpstan-rich-parser.neon',
21-
]);
32+
__DIR__ . '/../../vendor/larastan/larastan/extension.neon',
33+
];
2234
}
2335
}

tests/PHPStan/Laravel/Migrations/RequireWithoutTimestampRule/Fixture/Models/TestUser.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace Worksome\CodingStyle\Tests\PHPStan\Laravel\Migrations\RequireWithoutTimestampRule\Fixture\Models;
44

55
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Eloquent\SoftDeletes;
67

78
class TestUser extends Model
89
{
10+
use SoftDeletes;
911
}

tests/PHPStan/Laravel/Migrations/RequireWithoutTimestampRule/Fixture/database/migrations/invalid_migration.php.inc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ return new class extends Migration
2525
'test' => 'test',
2626
]);
2727

28-
$testUser = TestUser::make(['test' => 'test']);
29-
$testUser->save();
28+
TestUser::make(['test' => 'test'])->save();
3029

31-
$testUser2 = TestUser::make(['test' => 'test']);
32-
$testUser2->saveQuietly();
30+
TestUser::make(['test' => 'test'])->saveQuietly();
3331

3432
TestUser::where('test', 'test')
3533
->delete();

tests/PHPStan/Laravel/Migrations/RequireWithoutTimestampRule/Fixture/database/migrations/invalid_migration_with_multiple_models.php.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ return new class extends Migration
1818
->nullable();
1919
});
2020

21-
TestClient::withTrashed()->update([
21+
TestClient::update([
2222
'test' => 'test',
2323
]);
2424

tests/PHPStan/Laravel/Migrations/RequireWithoutTimestampRule/RequireWithoutTimestampsRuleTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,20 @@
3333
24,
3434
],
3535
[
36-
"Line 29: The 'save()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
37-
29,
36+
"Line 28: The 'save()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
37+
28,
3838
],
3939
[
40-
"Line 32: The 'saveQuietly()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
41-
32,
40+
"Line 30: The 'saveQuietly()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
41+
30,
4242
],
4343
[
44-
"Line 34: The 'delete()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
45-
34,
44+
"Line 32: The 'delete()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
45+
32,
4646
],
4747
[
48-
"Line 37: The 'restore()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
49-
37,
48+
"Line 35: The 'restore()' method call should be within 'withoutTimestamps()' context to prevent unintended timestamp updates.",
49+
35,
5050
],
5151
],
5252
'invalid migration with multiple models' => [

0 commit comments

Comments
 (0)