diff --git a/config/sets/laravel90.php b/config/sets/laravel90.php index ba558cee..a90e3bfb 100644 --- a/config/sets/laravel90.php +++ b/config/sets/laravel90.php @@ -10,6 +10,7 @@ use Rector\Renaming\ValueObject\MethodCallRename; use Rector\Visibility\Rector\ClassMethod\ChangeMethodVisibilityRector; use Rector\Visibility\ValueObject\ChangeMethodVisibility; +use RectorLaravel\Rector\Class_\AddExtendsAnnotationToModelFactoriesRector; use RectorLaravel\Rector\PropertyFetch\ReplaceFakerInstanceWithHelperRector; # see https://laravel.com/docs/9.x/upgrade @@ -70,6 +71,9 @@ // https://github.com/laravel/framework/commit/7746337149a7ffd6b4a862d9bd54593cf3520708 $rectorConfig->rule(ReplaceFakerInstanceWithHelperRector::class); + // https://github.com/laravel/framework/pull/39169 + $rectorConfig->rule(AddExtendsAnnotationToModelFactoriesRector::class); + $rectorConfig ->ruleWithConfiguration(RenameMethodRector::class, [ // https://github.com/laravel/framework/commit/9b4f011fb95c70444812f61d46c8e21fb5b66dd9 diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index e2374ad3..bd84a5e0 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 37 Rules Overview +# 39 Rules Overview ## AddArgumentDefaultValueRector @@ -40,6 +40,26 @@ return static function (RectorConfig $rectorConfig): void {
+## AddExtendsAnnotationToModelFactoriesRector + +Adds the `@extends` annotation to Factories. + +- class: [`RectorLaravel\Rector\Class_\AddExtendsAnnotationToModelFactoriesRector`](../src/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector.php) + +```diff + use Illuminate\Database\Eloquent\Factories\Factory; + ++/** ++ * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> ++ */ + class UserFactory extends Factory + { + protected $model = \App\Models\User::class; + } +``` + +
+ ## AddGenericReturnTypeToRelationsRector Add generic return type to relations in child of `Illuminate\Database\Eloquent\Model` @@ -795,6 +815,23 @@ It will removes the dump data just like dd or dump functions from the code.`
+## RemoveModelPropertyFromFactoriesRector + +Removes the `$model` property from Factories. + +- class: [`RectorLaravel\Rector\Class_\RemoveModelPropertyFromFactoriesRector`](../src/Rector/Class_/RemoveModelPropertyFromFactoriesRector.php) + +```diff + use Illuminate\Database\Eloquent\Factories\Factory; + + class UserFactory extends Factory + { +- protected $model = \App\Models\User::class; + } +``` + +
+ ## ReplaceFakerInstanceWithHelperRector Replace `$this->faker` with the `fake()` helper function in Factories diff --git a/src/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector.php b/src/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector.php new file mode 100644 index 00000000..682b10ef --- /dev/null +++ b/src/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector.php @@ -0,0 +1,137 @@ + + */ +class UserFactory extends Factory +{ + protected $model = \App\Models\User::class; +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isObjectType($node, new ObjectType(self::FACTORY_CLASS_NAME))) { + return null; + } + + foreach ($node->stmts as $stmt) { + if (! $stmt instanceof Property) { + continue; + } + + if (! $this->isName($stmt, 'model')) { + continue; + } + + $this->addExtendsPhpDocTag($node, $stmt); + + break; + } + + return $node; + } + + public function addExtendsPhpDocTag(Node $node, Property $property): void + { + if ($property->props === []) { + return; + } + + $modelName = $this->getModelName($property->props[0]->default); + + if ($modelName === null) { + return; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + if ($phpDocInfo->hasByName(self::EXTENDS_TAG_NAME)) { + return; + } + + $phpDocTagNode = new PhpDocTagNode(self::EXTENDS_TAG_NAME, new ExtendsTagValueNode( + new GenericTypeNode( + new FullyQualifiedIdentifierTypeNode(self::FACTORY_CLASS_NAME), + [new FullyQualifiedIdentifierTypeNode($modelName)] + ), + '' + )); + + $phpDocInfo->addPhpDocTagNode($phpDocTagNode); + } + + private function getModelName(?Expr $defaultProp): ?string + { + if ($defaultProp instanceof ClassConstFetch) { + return $this->getName($defaultProp->class); + } + + if ($defaultProp instanceof String_) { + return $defaultProp->value; + } + + return null; + } +} diff --git a/src/Rector/Class_/RemoveModelPropertyFromFactoriesRector.php b/src/Rector/Class_/RemoveModelPropertyFromFactoriesRector.php new file mode 100644 index 00000000..bc890446 --- /dev/null +++ b/src/Rector/Class_/RemoveModelPropertyFromFactoriesRector.php @@ -0,0 +1,80 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Factories\Factory'))) { + return null; + } + + foreach ($node->stmts as $index => $stmt) { + if (! $stmt instanceof Property) { + continue; + } + + if (! $this->isName($stmt, 'model')) { + continue; + } + + unset($node->stmts[$index]); + + break; + } + + return $node; + } +} diff --git a/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/AddExtendsAnnotationToModelFactoriesRectorTest.php b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/AddExtendsAnnotationToModelFactoriesRectorTest.php new file mode 100644 index 00000000..03be9229 --- /dev/null +++ b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/AddExtendsAnnotationToModelFactoriesRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/fixture.php.inc b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/fixture.php.inc new file mode 100644 index 00000000..129bd4d2 --- /dev/null +++ b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/fixture.php.inc @@ -0,0 +1,28 @@ + +----- + + */ +class UserFactory extends Factory +{ + protected $model = \App\Models\User::class; +} + +?> diff --git a/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/skip_existing_extends_tag.php.inc b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/skip_existing_extends_tag.php.inc new file mode 100644 index 00000000..230c16ba --- /dev/null +++ b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/skip_existing_extends_tag.php.inc @@ -0,0 +1,15 @@ + + */ +class UserFactory extends Factory +{ + protected $model = \App\Models\User::class; +} + +?> diff --git a/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/using_class_string_as_model_type.php.inc b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/using_class_string_as_model_type.php.inc new file mode 100644 index 00000000..996e194c --- /dev/null +++ b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/Fixture/using_class_string_as_model_type.php.inc @@ -0,0 +1,28 @@ + +----- + + */ +class UserFactory extends Factory +{ + protected $model = 'App\Models\User'; +} + +?> diff --git a/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/config/configured_rule.php b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/config/configured_rule.php new file mode 100644 index 00000000..5208b5c2 --- /dev/null +++ b/tests/Rector/Class_/AddExtendsAnnotationToModelFactoriesRector/config/configured_rule.php @@ -0,0 +1,13 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->rule(AddExtendsAnnotationToModelFactoriesRector::class); +}; diff --git a/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/Fixture/fixture.php.inc b/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/Fixture/fixture.php.inc new file mode 100644 index 00000000..8c0f8d5b --- /dev/null +++ b/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/Fixture/fixture.php.inc @@ -0,0 +1,24 @@ + +----- + diff --git a/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/RemoveModelPropertyFromFactoriesRectorTest.php b/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/RemoveModelPropertyFromFactoriesRectorTest.php new file mode 100644 index 00000000..8600366c --- /dev/null +++ b/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/RemoveModelPropertyFromFactoriesRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/config/configured_rule.php b/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/config/configured_rule.php new file mode 100644 index 00000000..d60363e2 --- /dev/null +++ b/tests/Rector/Class_/RemoveModelPropertyFromFactoriesRector/config/configured_rule.php @@ -0,0 +1,13 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->rule(RemoveModelPropertyFromFactoriesRector::class); +};