Skip to content

Commit 75a098f

Browse files
authored
Add a Rector Rule to change the argument type from array to ArrayCollection for the setParameters ORM QueryBuilder method (#326)
1 parent 04998ca commit 75a098f

File tree

8 files changed

+310
-0
lines changed

8 files changed

+310
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
4+
5+
final class QueryBuilderCall
6+
{
7+
public function createCustomQueryBuilder()
8+
{
9+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
10+
$ormQueryBuilder->setParameters([
11+
new \Doctrine\ORM\Query\Parameter('foo', 'bar'),
12+
new \Doctrine\ORM\Query\Parameter('bar', 1),
13+
new \Doctrine\ORM\Query\Parameter('baz', false),
14+
]);
15+
}
16+
}
17+
?>
18+
-----
19+
<?php
20+
21+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
22+
23+
final class QueryBuilderCall
24+
{
25+
public function createCustomQueryBuilder()
26+
{
27+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
28+
$ormQueryBuilder->setParameters(new \Doctrine\Common\Collections\ArrayCollection([new \Doctrine\ORM\Query\Parameter('foo', 'bar'), new \Doctrine\ORM\Query\Parameter('bar', 1), new \Doctrine\ORM\Query\Parameter('baz', false)]));
29+
}
30+
}
31+
?>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
4+
5+
final class QueryBuilderCall
6+
{
7+
public function createCustomQueryBuilder()
8+
{
9+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
10+
$ormQueryBuilder->setParameters(new \Doctrine\Common\Collections\ArrayCollection([
11+
'foo' => 'bar',
12+
'bar' => 1,
13+
'baz' => false
14+
]));
15+
}
16+
}
17+
?>
18+
-----
19+
<?php
20+
21+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
22+
23+
final class QueryBuilderCall
24+
{
25+
public function createCustomQueryBuilder()
26+
{
27+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
28+
$ormQueryBuilder->setParameters(new \Doctrine\Common\Collections\ArrayCollection([new \Doctrine\ORM\Query\Parameter('foo', 'bar'), new \Doctrine\ORM\Query\Parameter('bar', 1), new \Doctrine\ORM\Query\Parameter('baz', false)]));
29+
}
30+
}
31+
?>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
4+
5+
final class QueryBuilderCall
6+
{
7+
public function createCustomQueryBuilder()
8+
{
9+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
10+
$ormQueryBuilder->setParameters([
11+
'foo' => 'bar',
12+
'bar' => 1,
13+
'baz' => false
14+
]);
15+
}
16+
}
17+
?>
18+
-----
19+
<?php
20+
21+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
22+
23+
final class QueryBuilderCall
24+
{
25+
public function createCustomQueryBuilder()
26+
{
27+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
28+
$ormQueryBuilder->setParameters(new \Doctrine\Common\Collections\ArrayCollection([new \Doctrine\ORM\Query\Parameter('foo', 'bar'), new \Doctrine\ORM\Query\Parameter('bar', 1), new \Doctrine\ORM\Query\Parameter('baz', false)]));
29+
}
30+
}
31+
?>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
4+
5+
final class QueryBuilderCall
6+
{
7+
public function createCustomQueryBuilder()
8+
{
9+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
10+
$ormQueryBuilder->setParameters(new \Doctrine\Common\Collections\ArrayCollection([
11+
new \Doctrine\ORM\Query\Parameter('foo', 'bar'),
12+
new \Doctrine\ORM\Query\Parameter('bar', 1),
13+
new \Doctrine\ORM\Query\Parameter('baz', false),
14+
]));
15+
}
16+
}
17+
?>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
4+
5+
final class QueryBuilderCall
6+
{
7+
public function createCustomQueryBuilderWithNumeric()
8+
{
9+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
10+
$ormQueryBuilder->setParameters([
11+
'one',
12+
'two',
13+
'three'
14+
]);
15+
}
16+
}
17+
?>
18+
-----
19+
<?php
20+
21+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector\Fixture;
22+
23+
final class QueryBuilderCall
24+
{
25+
public function createCustomQueryBuilderWithNumeric()
26+
{
27+
$ormQueryBuilder = new \Doctrine\ORM\QueryBuilder();
28+
$ormQueryBuilder->setParameters(new \Doctrine\Common\Collections\ArrayCollection([new \Doctrine\ORM\Query\Parameter(0, 'one'), new \Doctrine\ORM\Query\Parameter(1, 'two'), new \Doctrine\ORM\Query\Parameter(2, 'three')]));
29+
}
30+
}
31+
?>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\Tests\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class SetParametersArrayToCollectionRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/configured_rule.php';
27+
}
28+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
7+
return RectorConfig::configure()
8+
->withRules([\Rector\Doctrine\Orm30\Rector\MethodCall\SetParametersArrayToCollectionRector::class]);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\Orm30\Rector\MethodCall;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Arg;
9+
use PhpParser\Node\Expr\Array_;
10+
use PhpParser\Node\Expr\ArrayItem;
11+
use PhpParser\Node\Expr\MethodCall;
12+
use PhpParser\Node\Expr\New_;
13+
use PhpParser\Node\Name\FullyQualified;
14+
use PhpParser\Node\Scalar\LNumber;
15+
use PHPStan\Type\ObjectType;
16+
use Rector\Rector\AbstractRector;
17+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
18+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
19+
20+
/**
21+
* @see https://github.com/doctrine/orm/pull/9490
22+
* @see https://github.com/doctrine/orm/blob/3.0.x/UPGRADE.md#query-querybuilder-and-nativequery-parameters-bc-break
23+
*/
24+
final class SetParametersArrayToCollectionRector extends AbstractRector
25+
{
26+
public function getRuleDefinition(): RuleDefinition
27+
{
28+
return new RuleDefinition(
29+
'Change the argument type for setParameters from array to ArrayCollection and Parameter calls',
30+
[
31+
new CodeSample(
32+
<<<'CODE_SAMPLE'
33+
$entityManager->createQueryBuilder()->setParameters([
34+
'foo' => 'bar'
35+
]);
36+
CODE_SAMPLE
37+
,
38+
<<<'CODE_SAMPLE'
39+
$entityManager->createQueryBuilder()->setParameters(new \Doctrine\Common\Collections\ArrayCollection([
40+
new \Doctrine\ORM\Query\Parameter('foo', 'bar')
41+
]));
42+
CODE_SAMPLE
43+
)]
44+
);
45+
}
46+
47+
/**
48+
* @return array<class-string<Node>>
49+
*/
50+
public function getNodeTypes(): array
51+
{
52+
return [MethodCall::class];
53+
}
54+
55+
/**
56+
* @param MethodCall $node
57+
*/
58+
public function refactor(Node $node): Node|null
59+
{
60+
$varType = $this->nodeTypeResolver->getType($node->var);
61+
62+
if (! $varType instanceof ObjectType) {
63+
return null;
64+
}
65+
66+
if (! $varType->isInstanceOf('Doctrine\\ORM\\QueryBuilder')->yes()) {
67+
return null;
68+
}
69+
70+
if ($node->isFirstClassCallable()) {
71+
return null;
72+
}
73+
74+
if (! $this->isNames($node->name, ['setParameters'])) {
75+
return null;
76+
}
77+
78+
$args = $node->getArgs();
79+
if (\count($args) !== 1) {
80+
return null;
81+
}
82+
83+
$currentArg = $args[0]->value;
84+
$isAlreadyAnArrayCollection = false;
85+
86+
$currentArgType = $this->nodeTypeResolver->getType($currentArg);
87+
if (
88+
$currentArgType instanceof ObjectType
89+
&& $currentArgType->isInstanceOf('Doctrine\\Common\\Collections\\ArrayCollection')
90+
->yes()
91+
&& $currentArg instanceof New_
92+
&& count($currentArg->args) === 1
93+
&& $currentArg->args[0] instanceof Arg
94+
) {
95+
$currentArg = $currentArg->args[0]->value;
96+
$isAlreadyAnArrayCollection = true;
97+
}
98+
99+
if (! $currentArg instanceof Array_) {
100+
return null;
101+
}
102+
103+
$changedParameterType = false;
104+
$parameters = [];
105+
foreach ($currentArg->items as $index => $value) {
106+
if (! $value instanceof ArrayItem) {
107+
return null;
108+
}
109+
110+
$arrayValueType = $this->nodeTypeResolver->getType($value->value);
111+
if (! $arrayValueType instanceof ObjectType || ! $arrayValueType->isInstanceOf(
112+
'Doctrine\\ORM\\Query\\Parameter'
113+
)->yes()) {
114+
$newParameter = new New_(new FullyQualified('Doctrine\\ORM\\Query\\Parameter'));
115+
$newParameter->args = [new Arg($value->key ?? new LNumber($index)), new Arg($value->value)];
116+
$value->value = $newParameter;
117+
$changedParameterType = true;
118+
}
119+
120+
$parameters[] = new ArrayItem($value->value);
121+
}
122+
123+
if ($changedParameterType === false && $isAlreadyAnArrayCollection) {
124+
return null;
125+
}
126+
127+
$newCollection = new New_(new FullyQualified('Doctrine\\Common\\Collections\\ArrayCollection'));
128+
$newCollection->args = [new Arg(new Array_($parameters))];
129+
130+
$node->args = [new Arg($newCollection)];
131+
return $node;
132+
}
133+
}

0 commit comments

Comments
 (0)