Skip to content

Commit

Permalink
Add new rule - ExplicitRelationCollectionRector
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Mar 21, 2024
1 parent a1d50bb commit 97a5d73
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Rector\Doctrine\Tests\CodeQuality\Rector\Class_\ExplicitRelationCollectionRector;

use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class ExplicitRelationCollectionRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): \Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Rector\Doctrine\Tests\CodeQuality\Rector\Class_\ExplicitRelationCollectionRector\Fixture;

use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\OneToMany;

#[Entity]
class SomeClass
{
#[OneToMany(targetEntity: 'SomeClass')]
private $items = [];
}

?>
-----
<?php

namespace Rector\Doctrine\Tests\CodeQuality\Rector\Class_\ExplicitRelationCollectionRector\Fixture;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\OneToMany;

#[Entity]
class SomeClass
{
#[OneToMany(targetEntity: 'SomeClass')]
private \Doctrine\Common\Collections\Collection $items;

public function __construct()
{
$this->items = new ArrayCollection();
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Doctrine\CodeQuality\Rector\Class_\ExplicitRelationCollectionRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(ExplicitRelationCollectionRector::class);
};
100 changes: 100 additions & 0 deletions rules/CodeQuality/Rector/Class_/ExplicitRelationCollectionRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace Rector\Doctrine\CodeQuality\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use Rector\Doctrine\NodeAnalyzer\AttrinationFinder;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Doctrine\Tests\CodeQuality\Rector\Class_\ExplicitRelationCollectionRector\ExplicitRelationCollectionRectorTest
*/
final class ExplicitRelationCollectionRector extends AbstractRector
{
public function __construct(
private readonly AttrinationFinder $attrinationFinder
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Use explicit collection in one-to-many relations of Doctrine entity', [
new CodeSample(
<<<'CODE_SAMPLE'
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\Entity;
#[Entity]
class SomeClass
{
#[OneToMany(targetEntity: 'SomeClass')]
private $items = [];
}
CODE_SAMPLE

,
<<<'CODE_SAMPLE'
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[Entity]
class SomeClass
{
#[OneToMany(targetEntity: 'SomeClass')]
private Collection $items;
public function __construct()
{
$this->items = new ArrayCollection();
}
}
CODE_SAMPLE
),
]);
}

/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class];
}

/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->attrinationFinder->hasByOne($node, 'Doctrine\ORM\Mapping\Entity')) {
return null;
}

foreach ($node->getProperties() as $property) {
if (! $this->attrinationFinder->hasByOne($property, 'Doctrine\ORM\Mapping\OneToMany')) {
continue;
}

// make sure has collection
if (! $property->type instanceof Node) {
$property->type = new FullyQualified('Doctrine\Common\Collections\Collection');
}

// make sure is null
if ($property->props[0]->default instanceof Expr) {
$property->props[0]->default = null;
}
}

return $node;
}
}

0 comments on commit 97a5d73

Please sign in to comment.