Skip to content

Commit 5df2683

Browse files
Check valid field names are used in orderBy parameter of findBy and findOneBy repository methods.
1 parent 934f573 commit 5df2683

File tree

4 files changed

+116
-0
lines changed

4 files changed

+116
-0
lines changed

src/Rules/Doctrine/ORM/RepositoryMethodCallRule.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,37 @@ public function processNode(Node $node, Scope $scope): array
8787
}
8888
}
8989

90+
$orderByType = count($node->getArgs()) > 1 ? $scope->getType($node->getArgs()[1]->value) : null;
91+
92+
if (
93+
in_array($methodName, [
94+
'findBy',
95+
'findOneBy',
96+
], true)
97+
&& $orderByType !== null
98+
) {
99+
foreach ($orderByType->getConstantArrays() as $constantArray) {
100+
foreach ($constantArray->getKeyTypes() as $keyType) {
101+
foreach ($keyType->getConstantStrings() as $fieldName) {
102+
if (
103+
$classMetadata->hasField($fieldName->getValue())
104+
|| $classMetadata->hasAssociation($fieldName->getValue())
105+
) {
106+
continue;
107+
}
108+
109+
$messages[] = RuleErrorBuilder::message(sprintf(
110+
'Call to method %s::%s() - entity %s does not have a field named $%s.',
111+
$calledOnType->describe(VerbosityLevel::typeOnly()),
112+
$methodName,
113+
$entityClassNames[0],
114+
$fieldName->getValue(),
115+
))->identifier(sprintf('doctrine.%sArgument', $methodName))->build();
116+
}
117+
}
118+
}
119+
}
120+
90121
return $messages;
91122
}
92123

tests/Rules/Doctrine/ORM/RepositoryMethodCallRuleTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ public function testRule(): void
7676
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::count() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
7777
45,
7878
],
79+
[
80+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
81+
54,
82+
],
83+
[
84+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
85+
55,
86+
],
87+
[
88+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
89+
56,
90+
],
91+
[
92+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
93+
56,
94+
],
95+
[
96+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
97+
64,
98+
],
99+
[
100+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
101+
65,
102+
],
103+
[
104+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
105+
66,
106+
],
107+
[
108+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
109+
66,
110+
],
79111
]);
80112
}
81113

tests/Rules/Doctrine/ORM/RepositoryMethodCallRuleWithoutObjectManagerLoaderTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ public function testRule(): void
7676
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::count() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
7777
45,
7878
],
79+
[
80+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
81+
54,
82+
],
83+
[
84+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
85+
55,
86+
],
87+
[
88+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
89+
56,
90+
],
91+
[
92+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
93+
56,
94+
],
95+
[
96+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
97+
64,
98+
],
99+
[
100+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
101+
65,
102+
],
103+
[
104+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
105+
66,
106+
],
107+
[
108+
'Call to method Doctrine\ORM\EntityRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
109+
66,
110+
],
79111
]);
80112
}
81113

tests/Rules/Doctrine/ORM/data/repository-findBy-etc.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,25 @@ public function doCountBy(): void
4545
$entityRepository->count(['nonexistent' => 'test', 'transient' => 'test']);
4646
}
4747

48+
49+
public function doFindByOrder(): void
50+
{
51+
$entityRepository = $this->entityManager->getRepository(MyEntity::class);
52+
$entityRepository->findBy([], ['id' => 'ASC']);
53+
$entityRepository->findBy([], ['title' => 'DESC']);
54+
$entityRepository->findBy([], ['transient' => 'ASC']);
55+
$entityRepository->findBy([], ['nonexistent' => 'DESC']);
56+
$entityRepository->findBy([], ['nonexistent' => 'ASC', 'transient' => 'DESC']);
57+
}
58+
59+
public function doFindOneByOrder(): void
60+
{
61+
$entityRepository = $this->entityManager->getRepository(MyEntity::class);
62+
$entityRepository->findOneBy([], ['id' => 'ASC']);
63+
$entityRepository->findOneBy([], ['title' => 'DESC']);
64+
$entityRepository->findOneBy([], ['transient' => 'ASC']);
65+
$entityRepository->findOneBy([], ['nonexistent' => 'DESC']);
66+
$entityRepository->findOneBy([], ['nonexistent' => 'ASC', 'transient' => 'DESC']);
67+
}
68+
4869
}

0 commit comments

Comments
 (0)