diff --git a/config/sets/laravel-legacy-factories-to-classes.php b/config/sets/laravel-legacy-factories-to-classes.php index 471dc45c..041737c8 100644 --- a/config/sets/laravel-legacy-factories-to-classes.php +++ b/config/sets/laravel-legacy-factories-to-classes.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; +use RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector; use RectorLaravel\Rector\FuncCall\FactoryFuncCallToStaticCallRector; use RectorLaravel\Rector\MethodCall\FactoryApplyingStatesRector; use RectorLaravel\Rector\Namespace_\FactoryDefinitionRector; @@ -13,6 +14,7 @@ // https://laravel.com/docs/7.x/database-testing#writing-factories // https://laravel.com/docs/8.x/database-testing#defining-model-factories $rectorConfig->rule(FactoryDefinitionRector::class); + $rectorConfig->rule(AddHasFactoryToModelsRector::class); // https://laravel.com/docs/7.x/database-testing#using-factories // https://laravel.com/docs/8.x/database-testing#creating-models-using-factories diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index e7dd7ca8..3ef6f11c 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -59,6 +59,28 @@ Adds the `@extends` annotation to Factories.
+## AddHasFactoryToModelsRector + +Adds the `HasFactory` trait to Models. + +:wrench: **configure it!** + +- class: [`RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector`](../src/Rector/Class_/AddHasFactoryToModelsRector.php) + +```diff + namespace App\Models; + + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Database\Eloquent\Model; + + class User extends Model + { ++ use \Illuminate\Database\Eloquent\Factories\HasFactory; + } +``` + +
+ ## AddGenericReturnTypeToRelationsRector Add generic return type to relations in child of `Illuminate\Database\Eloquent\Model` diff --git a/src/Rector/Class_/AddHasFactoryToModelsRector.php b/src/Rector/Class_/AddHasFactoryToModelsRector.php new file mode 100644 index 00000000..00f45f3b --- /dev/null +++ b/src/Rector/Class_/AddHasFactoryToModelsRector.php @@ -0,0 +1,111 @@ +allowList = $configuration; + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($this->shouldSkipClass($node)) { + return null; + } + + $traitUse = new TraitUse([new FullyQualified(self::TRAIT_NAME)]); + + $node->stmts = array_merge([$traitUse], $node->stmts); + + return $node; + } + + private function shouldSkipClass(Class_ $class): bool + { + if (! $this->isObjectType($class, new ObjectType('Illuminate\Database\Eloquent\Model'))) { + return true; + } + + if ($this->allowList !== [] && ! $this->isNames($class, $this->allowList)) { + return true; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($class); + if (! $classReflection instanceof ClassReflection) { + return true; + } + + return $classReflection->hasTraitUse(self::TRAIT_NAME); + } +} diff --git a/stubs/Illuminate/Database/Eloquent/Factories/HasFactory.php b/stubs/Illuminate/Database/Eloquent/Factories/HasFactory.php new file mode 100644 index 00000000..20d9bd24 --- /dev/null +++ b/stubs/Illuminate/Database/Eloquent/Factories/HasFactory.php @@ -0,0 +1,9 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_with_configuration.php'; + } +} diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/AddHasFactoryToModelsRectorTest.php b/tests/Rector/Class_/AddHasFactoryToModelsRector/AddHasFactoryToModelsRectorTest.php new file mode 100644 index 00000000..3c80d0d7 --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/AddHasFactoryToModelsRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_without_configuration.php'; + } +} diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/Configured/fixture.php.inc b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/Configured/fixture.php.inc new file mode 100644 index 00000000..8fdd0284 --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/Configured/fixture.php.inc @@ -0,0 +1,24 @@ + +----- + diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/Configured/no_match.php.inc b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/Configured/no_match.php.inc new file mode 100644 index 00000000..158c49d9 --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/Configured/no_match.php.inc @@ -0,0 +1,7 @@ + diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/WithoutConfiguration/fixture.php.inc b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/WithoutConfiguration/fixture.php.inc new file mode 100644 index 00000000..0b1f6e9d --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/WithoutConfiguration/fixture.php.inc @@ -0,0 +1,24 @@ + +----- + diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/WithoutConfiguration/skip_if_already_present.php.inc b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/WithoutConfiguration/skip_if_already_present.php.inc new file mode 100644 index 00000000..c4302cf5 --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/Fixture/WithoutConfiguration/skip_if_already_present.php.inc @@ -0,0 +1,12 @@ + diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/config/configured_rule_with_configuration.php b/tests/Rector/Class_/AddHasFactoryToModelsRector/config/configured_rule_with_configuration.php new file mode 100644 index 00000000..a4cabedd --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/config/configured_rule_with_configuration.php @@ -0,0 +1,15 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->ruleWithConfiguration(AddHasFactoryToModelsRector::class, [ + 'RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\Configured\User', + 'RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRectorConfigured\Fixture\SkipIfAlreadyPresent', + ]); +}; diff --git a/tests/Rector/Class_/AddHasFactoryToModelsRector/config/configured_rule_without_configuration.php b/tests/Rector/Class_/AddHasFactoryToModelsRector/config/configured_rule_without_configuration.php new file mode 100644 index 00000000..3b256e5b --- /dev/null +++ b/tests/Rector/Class_/AddHasFactoryToModelsRector/config/configured_rule_without_configuration.php @@ -0,0 +1,12 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->rule(AddHasFactoryToModelsRector::class); +};