diff --git a/config/sets/laravel-eloquent-magic-method-to-query-builder.php b/config/sets/laravel-eloquent-magic-method-to-query-builder.php
new file mode 100644
index 00000000..2e7010e7
--- /dev/null
+++ b/config/sets/laravel-eloquent-magic-method-to-query-builder.php
@@ -0,0 +1,12 @@
+import(__DIR__ . '/../config.php');
+ $rectorConfig->rule(EloquentMagicMethodToQueryBuilderRector::class);
+};
diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md
index bd84a5e0..76deac53 100644
--- a/docs/rector_rules_overview.md
+++ b/docs/rector_rules_overview.md
@@ -1,4 +1,4 @@
-# 39 Rules Overview
+# 40 Rules Overview
## AddArgumentDefaultValueRector
@@ -458,6 +458,21 @@ Convert DB Expression `__toString()` calls to `getValue()` method calls.
+## EloquentMagicMethodToQueryBuilderRector
+
+Transform certain magic method calls on Eloquent Models into corresponding Query Builder method calls.
+
+- class: [`RectorLaravel\Rector\StaticCall\EloquentMagicMethodToQueryBuilderRector`](../src/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector.php)
+
+```diff
+-User::find(1);
+-User::where('email', 'test@test.com')->first();
++User::query()->find(1);
++User::query()->where('email', 'test@test.com')->first();
+```
+
+
+
## EmptyToBlankAndFilledFuncRector
Replace use of the unsafe `empty()` function with Laravel's safer `blank()` & `filled()` functions.
diff --git a/src/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector.php b/src/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector.php
new file mode 100644
index 00000000..3f4b3f2e
--- /dev/null
+++ b/src/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector.php
@@ -0,0 +1,134 @@
+find(1);
+CODE_SAMPLE
+ ),
+ ]);
+ }
+
+ /**
+ * @return array>
+ */
+ public function getNodeTypes(): array
+ {
+ return [StaticCall::class];
+ }
+
+ /**
+ * @param StaticCall $node
+ */
+ public function refactor(Node $node): ?Node
+ {
+ $resolvedType = $this->nodeTypeResolver->getType($node->class);
+
+ // like for variables, example "$namespace"
+ // @phpstan-ignore-next-line
+ if (! method_exists($resolvedType, 'getClassName')) {
+ return null;
+ }
+
+ $className = (string) $resolvedType->getClassName();
+ $originalClassName = $this->getName($node->class); // like "self" or "App\Models\User"
+
+ if (is_null($originalClassName)) {
+ return null;
+ }
+
+ // does not extend Eloquent Model
+ if (! is_subclass_of($className, Model::class)) {
+ return null;
+ }
+
+ if (! $node->name instanceof Identifier) {
+ return null;
+ }
+
+ $methodName = $node->name->toString();
+
+ // if not a magic method
+ if (! $this->isMagicMethod($className, $methodName)) {
+ return null;
+ }
+
+ // if method belongs to Eloquent Query Builder or Query Builder
+ if (! ($this->isPublicMethod(EloquentQueryBuilder::class, $methodName) ||
+ $this->isPublicMethod(QueryBuilder::class, $methodName)
+ )) {
+ return null;
+ }
+
+ $queryMethodCall = $this->nodeFactory->createStaticCall($originalClassName, 'query');
+
+ $newNode = $this->nodeFactory->createMethodCall($queryMethodCall, $methodName);
+ foreach ($node->args as $arg) {
+ $newNode->args[] = $arg;
+ }
+
+ return $newNode;
+ }
+
+ public function isMagicMethod(string $className, string $methodName): bool
+ {
+ try {
+ $reflectionMethod = new ReflectionMethod($className, $methodName);
+ } catch (ReflectionException $e) {
+ return true; // method does not exist => is magic method
+ }
+
+ return false; // not a magic method
+ }
+
+ public function isPublicMethod(string $className, string $methodName): bool
+ {
+ try {
+ $reflectionMethod = new ReflectionMethod($className, $methodName);
+
+ // if not public
+ if (! $reflectionMethod->isPublic()) {
+ return false;
+ }
+
+ // if static
+ if ($reflectionMethod->isStatic()) {
+ return false;
+ }
+ } catch (ReflectionException $e) {
+ return false; // method does not exist => is magic method
+ }
+
+ return true; // method exist
+ }
+}
diff --git a/src/Set/LaravelSetList.php b/src/Set/LaravelSetList.php
index ad10a1e5..f01c35a5 100644
--- a/src/Set/LaravelSetList.php
+++ b/src/Set/LaravelSetList.php
@@ -107,4 +107,9 @@ final class LaravelSetList implements SetListInterface
* @var string
*/
final public const LARAVEL_FACADE_ALIASES_TO_FULL_NAMES = __DIR__ . '/../../config/sets/laravel-facade-aliases-to-full-names.php';
+
+ /**
+ * @var string
+ */
+ final public const LARAVEL_ELOQUENT_MAGIC_METHOD_TO_QUERY_BUILDER = __DIR__ . '/../../config/sets/laravel-eloquent-magic-method-to-query-builder.php';
}
diff --git a/stubs/Illuminate/Database/Eloquent/Builder.php b/stubs/Illuminate/Database/Eloquent/Builder.php
new file mode 100644
index 00000000..7169443b
--- /dev/null
+++ b/stubs/Illuminate/Database/Eloquent/Builder.php
@@ -0,0 +1,16 @@
+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/StaticCall/EloquentMagicMethodToQueryBuilderRector/Fixture/fixture.php.inc b/tests/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector/Fixture/fixture.php.inc
new file mode 100644
index 00000000..cd25aa79
--- /dev/null
+++ b/tests/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector/Fixture/fixture.php.inc
@@ -0,0 +1,47 @@
+where('xxx', 'xxx')->first();
+ $user = User::publicMethodBelongsToQueryBuilder(1);
+
+ # not eligible
+ $user = User::privateMethodBelongsToQueryBuilder(1);
+ $user = User::protectedMethodBelongsToQueryBuilder(1);
+ $user = User::publicMethodNotBelongsToQueryBuilder(1);
+ $user = User::query()->publicMethodBelongsToEloquentQueryBuilder(1);
+ $user = User::query()->publicMethodBelongsToQueryBuilder(1);
+ $user = User::staticMethodBelongsToModel(1);
+ }
+}
+-----
+publicMethodBelongsToEloquentQueryBuilder(1)->where('xxx', 'xxx')->first();
+ $user = User::query()->publicMethodBelongsToQueryBuilder(1);
+
+ # not eligible
+ $user = User::privateMethodBelongsToQueryBuilder(1);
+ $user = User::protectedMethodBelongsToQueryBuilder(1);
+ $user = User::publicMethodNotBelongsToQueryBuilder(1);
+ $user = User::query()->publicMethodBelongsToEloquentQueryBuilder(1);
+ $user = User::query()->publicMethodBelongsToQueryBuilder(1);
+ $user = User::staticMethodBelongsToModel(1);
+ }
+}
diff --git a/tests/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector/config/configured_rule.php b/tests/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector/config/configured_rule.php
new file mode 100644
index 00000000..6841bc57
--- /dev/null
+++ b/tests/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector/config/configured_rule.php
@@ -0,0 +1,14 @@
+import(__DIR__ . '/../../../../../config/config.php');
+ $rectorConfig->importNames(importDocBlockNames: false);
+ $rectorConfig->importShortClasses(false);
+ $rectorConfig->rule(EloquentMagicMethodToQueryBuilderRector::class);
+};