Skip to content

Commit ea39192

Browse files
authored
magic method call findBy~, findOneBy~ should allow optional arguments
1 parent abf6068 commit ea39192

File tree

5 files changed

+81
-10
lines changed

5 files changed

+81
-10
lines changed

Diff for: src/Reflection/Doctrine/MagicRepositoryMethodReflection.php

+26-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
use PHPStan\Reflection\FunctionVariant;
77
use PHPStan\Reflection\MethodReflection;
88
use PHPStan\TrinaryLogic;
9+
use PHPStan\Type\ArrayType;
910
use PHPStan\Type\Generic\TemplateTypeMap;
11+
use PHPStan\Type\IntegerType;
1012
use PHPStan\Type\MixedType;
13+
use PHPStan\Type\NullType;
14+
use PHPStan\Type\StringType;
1115
use PHPStan\Type\Type;
16+
use PHPStan\Type\UnionType;
1217

1318
class MagicRepositoryMethodReflection implements MethodReflection
1419
{
@@ -70,13 +75,31 @@ public function getPrototype(): \PHPStan\Reflection\ClassMemberReflection
7075

7176
public function getVariants(): array
7277
{
78+
if (strpos($this->name, 'findBy') === 0) {
79+
$arguments = [
80+
new DummyParameter('argument', new MixedType(), false, null, false, null),
81+
new DummyParameter('orderBy', new UnionType([new ArrayType(new StringType(), new StringType()), new NullType()]), true, null, false, null),
82+
new DummyParameter('limit', new UnionType([new IntegerType(), new NullType()]), true, null, false, null),
83+
new DummyParameter('offset', new UnionType([new IntegerType(), new NullType()]), true, null, false, null),
84+
];
85+
} elseif (strpos($this->name, 'findOneBy') === 0) {
86+
$arguments = [
87+
new DummyParameter('argument', new MixedType(), false, null, false, null),
88+
new DummyParameter('orderBy', new UnionType([new ArrayType(new StringType(), new StringType()), new NullType()]), true, null, false, null),
89+
];
90+
} elseif (strpos($this->name, 'countBy') === 0) {
91+
$arguments = [
92+
new DummyParameter('argument', new MixedType(), false, null, false, null),
93+
];
94+
} else {
95+
throw new \PHPStan\ShouldNotHappenException();
96+
}
97+
7398
return [
7499
new FunctionVariant(
75100
TemplateTypeMap::createEmpty(),
76101
null,
77-
[
78-
new DummyParameter('parameter', new MixedType(), false, null, false, null),
79-
],
102+
$arguments,
80103
false,
81104
$this->type
82105
),

Diff for: stubs/EntityRepository.stub

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@ class EntityRepository implements ObjectRepository
2929
public function findAll();
3030

3131
/**
32-
* @phpstan-param mixed[] $criteria
33-
* @phpstan-param string[]|null $orderBy
32+
* @phpstan-param array<string, mixed> $criteria
33+
* @phpstan-param array<string, string>|null $orderBy
3434
* @phpstan-param int|null $limit
3535
* @phpstan-param int|null $offset
3636
* @phpstan-return TEntityClass[]
3737
*/
3838
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null);
3939

4040
/**
41-
* @phpstan-param mixed[] $criteria The criteria.
42-
* @phpstan-param mixed[]|null $orderBy
41+
* @phpstan-param array<string, mixed> $criteria The criteria.
42+
* @phpstan-param array<string, string>|null $orderBy
4343
* @phpstan-return TEntityClass|null
4444
*/
4545
public function findOneBy(array $criteria, array $orderBy = null);

Diff for: stubs/Persistence/ObjectRepository.stub

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ interface ObjectRepository
2020
public function findAll();
2121

2222
/**
23-
* @phpstan-param mixed[] $criteria
24-
* @phpstan-param string[]|null $orderBy
23+
* @phpstan-param array<string, mixed> $criteria
24+
* @phpstan-param array<string, string>|null $orderBy
2525
* @phpstan-param int|null $limit
2626
* @phpstan-param int|null $offset
2727
* @phpstan-return TEntityClass[]
2828
*/
2929
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null);
3030

3131
/**
32-
* @phpstan-param mixed[] $criteria The criteria.
32+
* @phpstan-param array<string, mixed> $criteria The criteria.
3333
* @phpstan-return TEntityClass|null
3434
*/
3535
public function findOneBy(array $criteria);

Diff for: tests/Rules/Doctrine/ORM/MagicRepositoryMethodCallRuleTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ public function testRule(): void
6868
'Call to an undefined method PHPStan\Rules\Doctrine\ORM\TestRepository<PHPStan\Rules\Doctrine\ORM\MySecondEntity>::findByNonexistent().',
6969
56,
7070
],
71+
[
72+
'Parameter #2 $orderBy of method Doctrine\Persistence\ObjectRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findByTitle() expects array<string, string>|null, array<int, string> given.',
73+
65,
74+
],
75+
[
76+
'Parameter #3 $limit of method Doctrine\Persistence\ObjectRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findByTitle() expects int|null, string given.',
77+
68,
78+
],
79+
[
80+
'Parameter #4 $offset of method Doctrine\Persistence\ObjectRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findByTitle() expects int|null, string given.',
81+
71,
82+
],
83+
[
84+
'Parameter #2 $orderBy of method Doctrine\Persistence\ObjectRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::findOneByTitle() expects array<string, string>|null, array<int, string> given.',
85+
79,
86+
],
87+
[
88+
'Method Doctrine\Persistence\ObjectRepository<PHPStan\Rules\Doctrine\ORM\MyEntity>::countByTitle() invoked with 2 parameters, 1 required.',
89+
85,
90+
],
7191
]);
7292
}
7393

Diff for: tests/Rules/Doctrine/ORM/data/magic-repository.php

+28
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,32 @@ public function doFindByWithRepository(): void
5656
$entityRepository->findByNonexistent('test');
5757
$entityRepository->findByCustomMethod();
5858
}
59+
60+
public function doFindByWithOptionals(): void
61+
{
62+
$entityRepository = $this->entityManager->getRepository(MyEntity::class);
63+
$entityRepository->findByTitle('test', ['id' => 'DESC']);
64+
$entityRepository->findByTitle('test', null);
65+
$entityRepository->findByTitle('test', [1 => 'DESC']);
66+
$entityRepository->findByTitle('test', ['id' => 'DESC'], 1);
67+
$entityRepository->findByTitle('test', ['id' => 'DESC'], null);
68+
$entityRepository->findByTitle('test', ['id' => 'DESC'], '1');
69+
$entityRepository->findByTitle('test', ['id' => 'DESC'], 1, 1);
70+
$entityRepository->findByTitle('test', ['id' => 'DESC'], 1, null);
71+
$entityRepository->findByTitle('test', ['id' => 'DESC'], 1, '1');
72+
}
73+
74+
public function doFindOneByWithOptionals(): void
75+
{
76+
$entityRepository = $this->entityManager->getRepository(MyEntity::class);
77+
$entityRepository->findOneByTitle('test', ['id' => 'DESC']);
78+
$entityRepository->findOneByTitle('test', null);
79+
$entityRepository->findOneByTitle('test', [1 => 'DESC']);
80+
}
81+
82+
public function doCountByWithOptionals(): void
83+
{
84+
$entityRepository = $this->entityManager->getRepository(MyEntity::class);
85+
$entityRepository->countByTitle('test', ['id' => 'DESC']);
86+
}
5987
}

0 commit comments

Comments
 (0)