diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md
index bef70236d..c5f6373aa 100644
--- a/docs/rector_rules_overview.md
+++ b/docs/rector_rules_overview.md
@@ -1,4 +1,4 @@
-# 84 Rules Overview
+# 86 Rules Overview
## AbortIfRector
@@ -208,6 +208,27 @@ Add `parent::register();` call to `register()` class method in child of `Illumin
+## AddUseAnnotationToHasFactoryTraitRector
+
+Adds `@use` annotation to HasFactory trait usage to provide better IDE support.
+
+:wrench: **configure it!**
+
+- class: [`RectorLaravel\Rector\Class_\AddUseAnnotationToHasFactoryTraitRector`](../src/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector.php)
+
+```diff
+ use Illuminate\Database\Eloquent\Model;
+ use Illuminate\Database\Eloquent\Factories\HasFactory;
+
+ class User extends Model
+ {
++ /** @use \Illuminate\Database\Eloquent\Factories\HasFactory<\Database\Factories\UserFactory> */
+ use HasFactory;
+ }
+```
+
+
+
## AnonymousMigrationsRector
Convert migrations to anonymous classes.
@@ -229,13 +250,19 @@ Convert migrations to anonymous classes.
## AppEnvironmentComparisonToParameterRector
-Replace `$app->environment() === 'local'` with `$app->environment('local')`
+Replace app environment comparison with parameter or method call
- class: [`RectorLaravel\Rector\Expr\AppEnvironmentComparisonToParameterRector`](../src/Rector/Expr/AppEnvironmentComparisonToParameterRector.php)
```diff
--$app->environment() === 'production';
-+$app->environment('production');
+-$app->environment() === 'local';
+-$app->environment() !== 'production';
+-$app->environment() === 'testing';
+-in_array($app->environment(), ['local', 'testing']);
++$app->isLocal();
++! $app->isProduction();
++$app->environment('testing');
++$app->environment(['local', 'testing']);
```
@@ -892,6 +919,32 @@ Changes middlewares from rule definitions from string to array notation.
+## MakeModelAttributesAndScopesProtectedRector
+
+Makes Model attributes and scopes protected
+
+- class: [`RectorLaravel\Rector\ClassMethod\MakeModelAttributesAndScopesProtectedRector`](../src/Rector/ClassMethod/MakeModelAttributesAndScopesProtectedRector.php)
+
+```diff
+ class User extends Model
+ {
+- public function foo(): Attribute
++ protected function foo(): Attribute
+ {
+ return Attribute::get(fn () => $this->bar);
+ }
+
+ #[Scope]
+- public function active(Builder $query): Builder
++ protected function active(Builder $query): Builder
+ {
+ return $query->where('active', true);
+ }
+ }
+```
+
+
+
## MigrateToSimplifiedAttributeRector
Migrate to the new Model attributes syntax
diff --git a/src/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector.php b/src/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector.php
new file mode 100644
index 000000000..a453b1277
--- /dev/null
+++ b/src/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector.php
@@ -0,0 +1,285 @@
+ */
+ use HasFactory;
+}
+CODE_SAMPLE,
+ [self::FACTORY_NAMESPACES => ['Database\\Factories']]
+ )]
+ );
+ }
+
+ public function configure(array $configuration): void
+ {
+ if ($configuration === []) {
+ $this->factoryNamespaces = ['Database\\Factories'];
+
+ return;
+ }
+
+ Assert::keyExists($configuration, self::FACTORY_NAMESPACES);
+ Assert::isArray($configuration[self::FACTORY_NAMESPACES]);
+ Assert::allString($configuration[self::FACTORY_NAMESPACES]);
+ $this->factoryNamespaces = $configuration[self::FACTORY_NAMESPACES];
+ }
+
+ /**
+ * @return array>
+ */
+ 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\Model'))) {
+ return null;
+ }
+
+ $hasChanged = false;
+
+ foreach ($node->stmts as $stmt) {
+ if (! $stmt instanceof TraitUse) {
+ continue;
+ }
+
+ if (! $this->hasHasFactoryTrait($stmt)) {
+ continue;
+ }
+
+ if ($this->addUsePhpDocTag($stmt, $node)) {
+ $hasChanged = true;
+ }
+ }
+
+ if ($hasChanged) {
+ return $node;
+ }
+
+ return null;
+ }
+
+ private function hasHasFactoryTrait(TraitUse $traitUse): bool
+ {
+ foreach ($traitUse->traits as $trait) {
+ $traitName = $this->getName($trait);
+ if ($traitName === self::HAS_FACTORY_TRAIT) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function addUsePhpDocTag(TraitUse $traitUse, Class_ $class): bool
+ {
+ $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($traitUse);
+
+ if ($phpDocInfo->hasByName(self::USE_TAG_NAME)) {
+ return false;
+ }
+
+ $factoryClassName = $this->resolveFactoryClassName($class);
+ if ($factoryClassName === null) {
+ return false;
+ }
+
+ $phpDocTagNode = new PhpDocTagNode(
+ self::USE_TAG_NAME,
+ new UsesTagValueNode(
+ new GenericTypeNode(
+ new FullyQualifiedIdentifierTypeNode(self::HAS_FACTORY_TRAIT),
+ [new FullyQualifiedIdentifierTypeNode($factoryClassName)]
+ ),
+ ''
+ )
+ );
+
+ $phpDocInfo->addPhpDocTagNode($phpDocTagNode);
+
+ $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($traitUse);
+
+ return true;
+ }
+
+ private function resolveFactoryClassName(Class_ $class): ?string
+ {
+ $factoryFromProperty = $this->getFactoryFromProperty($class);
+ if ($factoryFromProperty !== null) {
+ return $factoryFromProperty;
+ }
+
+ $className = $this->getName($class);
+ if ($className === null) {
+ return null;
+ }
+
+ $modelName = $this->nodeNameResolver->getShortName($className);
+
+ $factoryName = $modelName . 'Factory';
+
+ $currentNamespace = $class->namespacedName?->toString() ?? $className;
+
+ $factoryClassNames = $this->getPotentialFactoryClassNames($currentNamespace, $factoryName);
+
+ foreach ($factoryClassNames as $factoryClassName) {
+ if ($this->reflectionProvider->hasClass($factoryClassName)) {
+ return $factoryClassName;
+ }
+ }
+
+ return null;
+ }
+
+ private function getFactoryFromProperty(Class_ $class): ?string
+ {
+ foreach ($class->stmts as $stmt) {
+ if (! $stmt instanceof Property) {
+ continue;
+ }
+
+ if (! $this->isName($stmt, 'factory')) {
+ continue;
+ }
+
+ if ($stmt->props[0]->default === null) {
+ continue;
+ }
+
+ $defaultValue = $stmt->props[0]->default;
+
+ if ($defaultValue instanceof ClassConstFetch) {
+ $factoryClassName = $this->getName($defaultValue->class);
+ if ($factoryClassName !== null && $this->reflectionProvider->hasClass($factoryClassName)) {
+ return $factoryClassName;
+ }
+ }
+
+ if ($defaultValue instanceof String_) {
+ $factoryClassName = $defaultValue->value;
+ if ($this->reflectionProvider->hasClass($factoryClassName)) {
+ return $factoryClassName;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return string[]
+ */
+ private function getPotentialFactoryClassNames(string $modelNamespace, string $factoryName): array
+ {
+ $factoryClassNames = [];
+
+ foreach ($this->factoryNamespaces as $factoryNamespace) {
+ // Remove leading backslash if present
+ $factoryNamespace = ltrim($factoryNamespace, '\\');
+
+ if (str_contains($modelNamespace, '\\Models\\')) {
+ $modelsPosition = strpos($modelNamespace, '\\Models\\');
+ if ($modelsPosition === false) {
+ continue;
+ }
+ $afterModels = substr($modelNamespace, $modelsPosition + 8);
+
+ if (str_contains($afterModels, '\\')) {
+ $namespaceParts = explode('\\', $afterModels);
+ array_pop($namespaceParts);
+ $deepNamespace = implode('\\', $namespaceParts);
+
+ $factoryClassNames[] = '\\' . $factoryNamespace . '\\' . $deepNamespace . '\\' . $factoryName;
+ }
+ } elseif (str_contains($modelNamespace, 'App\\')) {
+ $appPosition = strpos($modelNamespace, 'App\\');
+ if ($appPosition === false) {
+ continue;
+ }
+ $afterApp = substr($modelNamespace, $appPosition + 4);
+
+ if (str_contains($afterApp, '\\')) {
+ $namespaceParts = explode('\\', $afterApp);
+ array_pop($namespaceParts);
+ $deepNamespace = implode('\\', $namespaceParts);
+
+ $factoryClassNames[] = '\\' . $factoryNamespace . '\\' . $deepNamespace . '\\' . $factoryName;
+ }
+ }
+
+ $factoryClassNames[] = '\\' . $factoryNamespace . '\\' . $factoryName;
+ }
+
+ return array_unique($factoryClassNames);
+ }
+}
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/AddUseAnnotationToHasFactoryTraitRectorTest.php b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/AddUseAnnotationToHasFactoryTraitRectorTest.php
new file mode 100644
index 000000000..a199b7110
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/AddUseAnnotationToHasFactoryTraitRectorTest.php
@@ -0,0 +1,31 @@
+doTestFile($filePath);
+ }
+
+ public function provideConfigFilePath(): string
+ {
+ return __DIR__ . '/config/configured_rule.php';
+ }
+}
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/deep_namespace.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/deep_namespace.php.inc
new file mode 100644
index 000000000..955b14aa0
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/deep_namespace.php.inc
@@ -0,0 +1,30 @@
+
+-----
+
+ */
+ use HasFactory;
+}
+
+?>
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/fixture.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/fixture.php.inc
new file mode 100644
index 000000000..4fbdbce58
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/fixture.php.inc
@@ -0,0 +1,30 @@
+
+-----
+
+ */
+ use HasFactory;
+}
+
+?>
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/models_namespace.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/models_namespace.php.inc
new file mode 100644
index 000000000..bc7b459c1
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/models_namespace.php.inc
@@ -0,0 +1,30 @@
+
+-----
+
+ */
+ use HasFactory;
+}
+
+?>
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_existing_annotation.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_existing_annotation.php.inc
new file mode 100644
index 000000000..14f152d6b
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_existing_annotation.php.inc
@@ -0,0 +1,14 @@
+ */
+ use HasFactory;
+}
+
+?>
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_factory_doesnt_exist.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_factory_doesnt_exist.php.inc
new file mode 100644
index 000000000..6b03e8918
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_factory_doesnt_exist.php.inc
@@ -0,0 +1,13 @@
+
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_non_model.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_non_model.php.inc
new file mode 100644
index 000000000..7bba133a5
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/skip_non_model.php.inc
@@ -0,0 +1,12 @@
+
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/with_factory_property.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/with_factory_property.php.inc
new file mode 100644
index 000000000..3e92f5fea
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/with_factory_property.php.inc
@@ -0,0 +1,34 @@
+
+-----
+
+ */
+ use HasFactory;
+
+ protected static string $factory = \RectorLaravel\Tests\Rector\Class_\AddUseAnnotationToHasFactoryTraitRector\Source\ProductFactory::class;
+}
+
+?>
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/with_factory_property_string.php.inc b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/with_factory_property_string.php.inc
new file mode 100644
index 000000000..b155ffa1b
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Fixture/with_factory_property_string.php.inc
@@ -0,0 +1,34 @@
+
+-----
+
+ */
+ use HasFactory;
+
+ protected static string $factory = 'RectorLaravel\Tests\Rector\Class_\AddUseAnnotationToHasFactoryTraitRector\Source\ProductFactory';
+}
+
+?>
diff --git a/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Source/ProductFactory.php b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Source/ProductFactory.php
new file mode 100644
index 000000000..4683f57a1
--- /dev/null
+++ b/tests/Rector/Class_/AddUseAnnotationToHasFactoryTraitRector/Source/ProductFactory.php
@@ -0,0 +1,13 @@
+ruleWithConfiguration(AddUseAnnotationToHasFactoryTraitRector::class, [
+ AddUseAnnotationToHasFactoryTraitRector::FACTORY_NAMESPACES => [
+ 'RectorLaravel\\Tests\\Rector\\Class_\\AddUseAnnotationToHasFactoryTraitRector\\Source',
+ ],
+ ]);
+};