-
Notifications
You must be signed in to change notification settings - Fork 103
Use whereKey query method where possible #401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RectorLaravel\Rector\MethodCall; | ||
|
|
||
| use PhpParser\Node; | ||
| use PhpParser\Node\Arg; | ||
| use PhpParser\Node\Expr\MethodCall; | ||
| use PhpParser\Node\Expr\PropertyFetch; | ||
| use PhpParser\Node\Expr\StaticCall; | ||
| use PhpParser\Node\Identifier; | ||
| use PhpParser\Node\Scalar\String_; | ||
| use RectorLaravel\AbstractRector; | ||
| use RectorLaravel\NodeAnalyzer\QueryBuilderAnalyzer; | ||
| use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||
| use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
|
||
| /** | ||
| * @see \RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\EloquentWhereIdToWhereKeyRectorTest | ||
| */ | ||
| final class EloquentWhereIdToWhereKeyRector extends AbstractRector | ||
| { | ||
| public function __construct( | ||
| private readonly QueryBuilderAnalyzer $queryBuilderAnalyzer | ||
| ) {} | ||
|
|
||
| public function getRuleDefinition(): RuleDefinition | ||
| { | ||
| return new RuleDefinition( | ||
| 'Refactor model calls to the primary key using the `whereKey` and `whereKeyNot` methods', | ||
| [ | ||
| new CodeSample( | ||
| <<<'CODE_SAMPLE' | ||
| User::where('id', '=', $user->id)->get(); | ||
| User::where('id', $user->id)->get(); | ||
| CODE_SAMPLE, | ||
| <<<'CODE_SAMPLE' | ||
| User::whereKey($user)->get(); | ||
| User::whereKey($user)->get(); | ||
| CODE_SAMPLE | ||
| ), | ||
| new CodeSample( | ||
| <<<'CODE_SAMPLE' | ||
| User::where('id', '!=', $user->id)->get(); | ||
| User::whereNot('id', $user->id)->get(); | ||
| CODE_SAMPLE, | ||
| <<<'CODE_SAMPLE' | ||
| User::whereKeyNot($user)->get(); | ||
| User::whereKeyNot($user)->get(); | ||
| CODE_SAMPLE | ||
| ), | ||
| ] | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @return array<class-string<Node>> | ||
| */ | ||
| public function getNodeTypes(): array | ||
| { | ||
| return [MethodCall::class, StaticCall::class]; | ||
| } | ||
|
|
||
| /** | ||
| * @param MethodCall|StaticCall $node | ||
| */ | ||
| public function refactor(Node $node): ?Node | ||
| { | ||
| if (! $node instanceof MethodCall && ! $node instanceof StaticCall) { | ||
| return null; | ||
| } | ||
|
|
||
| $isWhere = $this->queryBuilderAnalyzer->isMatchingCall($node, 'where'); | ||
| $isWhereNot = $this->queryBuilderAnalyzer->isMatchingCall($node, 'whereNot'); | ||
|
|
||
| if (! $isWhere && ! $isWhereNot) { | ||
| return null; | ||
| } | ||
|
|
||
| $args = $node->getArgs(); | ||
| $argCount = count($args); | ||
|
|
||
| if ($argCount === 2) { | ||
| return $this->refactorTwoArgumentWhere($node, $args, $isWhereNot); | ||
| } | ||
|
|
||
| if ($argCount === 3 && $isWhere) { | ||
| return $this->refactorThreeArgumentWhere($node, $args); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * @param Arg[] $args | ||
| */ | ||
| private function refactorTwoArgumentWhere(MethodCall|StaticCall $node, array $args, bool $isWhereNot): ?Node | ||
| { | ||
| if (! $args[0] instanceof Arg || ! $args[0]->value instanceof String_) { | ||
| return null; | ||
| } | ||
|
|
||
| $columnName = $args[0]->value->value; | ||
| if ($columnName !== 'id') { | ||
| return null; | ||
| } | ||
|
|
||
| if (! $args[1] instanceof Arg || ! $args[1]->value instanceof PropertyFetch) { | ||
| return null; | ||
| } | ||
|
|
||
| $propertyFetch = $args[1]->value; | ||
| if (! $this->isName($propertyFetch->name, 'id')) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of hardcoding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's an essential point. Not every system has the |
||
| return null; | ||
| } | ||
|
|
||
| // where() with 2 args uses '=' operator -> whereKey | ||
| // whereNot() with 2 args uses '!=' operator -> whereKeyNot | ||
| $newMethodName = $isWhereNot ? 'whereKeyNot' : 'whereKey'; | ||
| $node->name = new Identifier($newMethodName); | ||
| $node->args = [new Arg($propertyFetch->var)]; | ||
|
|
||
| return $node; | ||
| } | ||
|
|
||
| /** | ||
| * @param Arg[] $args | ||
| */ | ||
| private function refactorThreeArgumentWhere(MethodCall|StaticCall $node, array $args): ?Node | ||
| { | ||
| if (! $args[0] instanceof Arg || ! $args[0]->value instanceof String_) { | ||
| return null; | ||
| } | ||
|
|
||
| $columnName = $args[0]->value->value; | ||
| if ($columnName !== 'id') { | ||
| return null; | ||
| } | ||
|
|
||
| if (! $args[1] instanceof Arg || ! $args[1]->value instanceof String_) { | ||
| return null; | ||
| } | ||
|
|
||
| $operator = $args[1]->value->value; | ||
| if (! in_array($operator, ['=', '!='], true)) { | ||
| return null; | ||
| } | ||
|
|
||
| if (! $args[2] instanceof Arg || ! $args[2]->value instanceof PropertyFetch) { | ||
| return null; | ||
| } | ||
|
|
||
| $propertyFetch = $args[2]->value; | ||
| if (! $this->isName($propertyFetch->name, 'id')) { | ||
| return null; | ||
| } | ||
|
|
||
| $newMethodName = $operator === '=' ? 'whereKey' : 'whereKeyNot'; | ||
| $node->name = new Identifier($newMethodName); | ||
| $node->args = [new Arg($propertyFetch->var)]; | ||
|
|
||
| return $node; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector; | ||
|
|
||
| use Iterator; | ||
| use PHPUnit\Framework\Attributes\DataProvider; | ||
| use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
|
||
| final class EloquentWhereIdToWhereKeyRectorTest extends AbstractRectorTestCase | ||
| { | ||
| public static function provideData(): Iterator | ||
| { | ||
| return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
| } | ||
|
|
||
| /** | ||
| * @test | ||
| */ | ||
| #[DataProvider('provideData')] | ||
| public function test(string $filePath): void | ||
| { | ||
| $this->doTestFile($filePath); | ||
| } | ||
|
|
||
| public function provideConfigFilePath(): string | ||
| { | ||
| return __DIR__ . '/config/configured_rule.php'; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| <?php | ||
|
|
||
| namespace RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Fixture; | ||
|
|
||
| use RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Source\User; | ||
|
|
||
| class ChainedMethodsFixture | ||
| { | ||
| public function run($user) | ||
| { | ||
| User::where('id', '=', $user->id)->where('status', 'active')->get(); | ||
|
|
||
| $query = User::query() | ||
| ->where('name', 'John') | ||
| ->where('id', '=', $user->id) | ||
| ->where('email', 'LIKE', '%@example.com'); | ||
| } | ||
| } | ||
|
|
||
| ?> | ||
| ----- | ||
| <?php | ||
|
|
||
| namespace RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Fixture; | ||
|
|
||
| use RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Source\User; | ||
|
|
||
| class ChainedMethodsFixture | ||
| { | ||
| public function run($user) | ||
| { | ||
| User::whereKey($user)->where('status', 'active')->get(); | ||
|
|
||
| $query = User::query() | ||
| ->where('name', 'John')->whereKey($user) | ||
| ->where('email', 'LIKE', '%@example.com'); | ||
| } | ||
| } | ||
|
|
||
| ?> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| namespace RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Fixture; | ||
|
|
||
| use RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Source\User; | ||
|
|
||
| class SomeClass | ||
| { | ||
| public function run($user) | ||
| { | ||
| User::where('id', '=', $user->id)->get(); | ||
| User::where('id', '!=', $user->id)->get(); | ||
| } | ||
| } | ||
|
|
||
| ?> | ||
| ----- | ||
| <?php | ||
|
|
||
| namespace RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Fixture; | ||
|
|
||
| use RectorLaravel\Tests\Rector\MethodCall\EloquentWhereIdToWhereKeyRector\Source\User; | ||
|
|
||
| class SomeClass | ||
| { | ||
| public function run($user) | ||
| { | ||
| User::whereKey($user)->get(); | ||
| User::whereKeyNot($user)->get(); | ||
| } | ||
| } | ||
|
|
||
| ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can these be condensed into a single method? they look quite similar