Skip to content

Commit

Permalink
Fix missing extra import on NestedAnnotationToAttributeRector (#2989)
Browse files Browse the repository at this point in the history
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
Co-authored-by: GitHub Action <action@github.com>
  • Loading branch information
3 people committed Oct 15, 2022
1 parent ea25cb0 commit f4f758c
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 32 deletions.
13 changes: 9 additions & 4 deletions build/target-repository/docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5897,16 +5897,21 @@ Changed nested annotations to attributes
```php
use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector;
use Rector\Php80\ValueObject\AnnotationPropertyToAttributeClass;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(
NestedAnnotationToAttributeRector::class,
[[
new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinTable', [
'Doctrine\ORM\Mapping\JoinColumn',
'Doctrine\ORM\Mapping\InverseJoinColumn',
], false),
new NestedAnnotationToAttribute([
new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\JoinColumn', 'joinColumns', false),
new AnnotationPropertyToAttributeClass(
'Doctrine\ORM\Mapping\InverseJoinColumn',
'inverseJoinColumns',
false
),
], 'Doctrine\ORM\Mapping\JoinTable', false),
]]
);
};
Expand Down
5 changes: 5 additions & 0 deletions packages/NodeTypeResolver/Node/AttributeKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,9 @@ final class AttributeKey
* @var string
*/
public const REPRINT_RAW_VALUE = 'reprint_raw_value';

/**
* @var string
*/
public const EXTRA_USE_IMPORT = 'extra_use_import';
}
2 changes: 1 addition & 1 deletion packages/PhpAttribute/NodeFactory/AttributeNameFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function create(
return new Name($useAliasMetadata->getShortAttributeName());
}

// 3. the class is not aliased and is compeltelly new... return the FQN version
// 3. the class is not aliased and is completely new... return the FQN version
return new FullyQualified($annotationToAttribute->getAttributeClass());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php80\ValueObject\AnnotationPropertyToAttributeClass;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
use Rector\PhpAttribute\AttributeArrayNameInliner;
Expand Down Expand Up @@ -80,7 +82,7 @@ public function createNested(
);
}

$nestedAttributeClass = $nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()[0];
$nestedAnnotationPropertyToAttributeClass = $nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()[0];

foreach ($doctrineAnnotationTagValueNode->values as $arrayItemNode) {
$nestedDoctrineAnnotationTagValueNode = $arrayItemNode->value;
Expand All @@ -96,7 +98,11 @@ public function createNested(
$attributeArgs = $this->createAttributeArgs($nestedArrayItemNode->value, $nestedAnnotationToAttribute);

$originalIdentifier = $doctrineAnnotationTagValueNode->identifierTypeNode->name;
$attributeName = $this->resolveAliasedAttributeName($originalIdentifier, $nestedAttributeClass);

$attributeName = $this->resolveAliasedAttributeName(
$originalIdentifier,
$nestedAnnotationPropertyToAttributeClass
);

$attribute = new Attribute($attributeName, $attributeArgs);
$attributeGroups[] = new AttributeGroup([$attribute]);
Expand Down Expand Up @@ -145,10 +151,14 @@ private function createArgsFromItems(
*/
private function resolveAliasedAttributeName(
string $originalIdentifier,
string $nestedAttributeClass
AnnotationPropertyToAttributeClass $annotationPropertyToAttributeClass
): FullyQualified|Name {
/** @var string $shortDoctrineAttributeName */
$shortDoctrineAttributeName = Strings::after($nestedAttributeClass, '\\', -1);
$shortDoctrineAttributeName = Strings::after(
$annotationPropertyToAttributeClass->getAttributeClass(),
'\\',
-1
);

$matches = Strings::match($originalIdentifier, self::SHORT_ORM_ALIAS_REGEX);
if ($matches !== null) {
Expand All @@ -161,7 +171,7 @@ private function resolveAliasedAttributeName(
return new Name($shortDoctrineAttributeName);
}

return new FullyQualified($nestedAttributeClass);
return new FullyQualified($annotationPropertyToAttributeClass->getAttributeClass());
}

/**
Expand All @@ -172,11 +182,9 @@ private function removeItems(
array $arrayItemNodes,
NestedAnnotationToAttribute $nestedAnnotationToAttribute
): array {
foreach (array_keys(
$nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()
) as $itemToRemoveName) {
foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $annotationPropertyToAttributeClass) {
foreach ($arrayItemNodes as $key => $arrayItemNode) {
if ($arrayItemNode->key !== $itemToRemoveName) {
if ($arrayItemNode->key !== $annotationPropertyToAttributeClass->getAnnotationProperty()) {
continue;
}

Expand All @@ -196,8 +204,11 @@ private function createFromExplicitProperties(
): array {
$attributeGroups = [];

foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $itemName => $nestedAttributeClass) {
$nestedArrayItemNode = $doctrineAnnotationTagValueNode->getValue($itemName);
foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $annotationPropertyToAttributeClass) {
/** @var string $annotationProperty */
$annotationProperty = $annotationPropertyToAttributeClass->getAnnotationProperty();

$nestedArrayItemNode = $doctrineAnnotationTagValueNode->getValue($annotationProperty);
if (! $nestedArrayItemNode instanceof ArrayItemNode) {
continue;
}
Expand All @@ -218,7 +229,18 @@ private function createFromExplicitProperties(
);

$originalIdentifier = $nestedDoctrineAnnotationTagValueNode->identifierTypeNode->name;
$attributeName = $this->resolveAliasedAttributeName($originalIdentifier, $nestedAttributeClass);

$attributeName = $this->resolveAliasedAttributeName(
$originalIdentifier,
$annotationPropertyToAttributeClass
);

if ($annotationPropertyToAttributeClass->doesNeedNewImport() && count($attributeName->parts) === 1) {
$attributeName->setAttribute(
AttributeKey::EXTRA_USE_IMPORT,
$annotationPropertyToAttributeClass->getAttributeClass()
);
}

$attribute = new Attribute($attributeName, $attributeArgs);
$attributeGroups[] = new AttributeGroup([$attribute]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Rector\Tests\Php80\Rector\Property\NestedAnnotationToAttributeRector\Fixture;

use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinTable;

final class DoctrineJoinColumnNestedInJoinTable
{
/**
* @JoinTable(name="lemma_type",
* inverseJoinColumns={@JoinColumn(name="lemma_id", referencedColumnName="lemma_id")}
* )
*/
private iterable $lemmas;
}

?>
-----
<?php

namespace Rector\Tests\Php80\Rector\Property\NestedAnnotationToAttributeRector\Fixture;

use Doctrine\ORM\Mapping\InverseJoinColumn;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinTable;

final class DoctrineJoinColumnNestedInJoinTable
{
#[JoinTable(name: 'lemma_type')]
#[InverseJoinColumn(name: 'lemma_id', referencedColumnName: 'lemma_id')]
private iterable $lemmas;
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,29 @@

use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector;
use Rector\Php80\ValueObject\AnnotationPropertyToAttributeClass;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(NestedAnnotationToAttributeRector::class, [
/** @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/attributes-reference.html#joincolumn-inversejoincolumn */
new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinTable', [
'joinColumns' => 'Doctrine\ORM\Mapping\JoinColumn',
'inverseJoinColumns' => 'Doctrine\ORM\Mapping\InverseJoinColumn',
new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\JoinColumn', 'joinColumns'),
new AnnotationPropertyToAttributeClass(
'Doctrine\ORM\Mapping\InverseJoinColumn',
'inverseJoinColumns',
true
),
]),

new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\Table', [
'indexes' => 'Doctrine\ORM\Mapping\Index',
'uniqueConstraints' => 'Doctrine\ORM\Mapping\UniqueConstraint',
new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\Index', 'indexes'),
new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\UniqueConstraint', 'uniqueConstraints'),
]),

/** @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#joincolumns */
new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinColumns', [
'Doctrine\ORM\Mapping\JoinColumn',
new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\JoinColumn'),
], true),
]);
};
30 changes: 28 additions & 2 deletions rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Naming\Naming\UseImportsResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php80\NodeFactory\NestedAttrGroupsFactory;
use Rector\Php80\ValueObject\AnnotationPropertyToAttributeClass;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;
use Rector\Php80\ValueObject\NestedDoctrineTagAndAnnotationToAttribute;
use Rector\PostRector\Collector\UseNodesToAddCollector;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
Expand All @@ -41,6 +45,7 @@ public function __construct(
private readonly UseImportsResolver $useImportsResolver,
private readonly PhpDocTagRemover $phpDocTagRemover,
private readonly NestedAttrGroupsFactory $nestedAttrGroupsFactory,
private readonly UseNodesToAddCollector $useNodesToAddCollector
) {
}

Expand Down Expand Up @@ -78,8 +83,11 @@ class SomeEntity
[
[
new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinTable', [
'joinColumns' => 'Doctrine\ORM\Mapping\JoinColumn',
'inverseJoinColumns' => 'Doctrine\ORM\Mapping\InverseJoinColumn',
new AnnotationPropertyToAttributeClass('Doctrine\ORM\Mapping\JoinColumn', 'joinColumns'),
new AnnotationPropertyToAttributeClass(
'Doctrine\ORM\Mapping\InverseJoinColumn',
'inverseJoinColumns'
),
]),
],
]
Expand Down Expand Up @@ -113,6 +121,7 @@ public function refactor(Node $node): ?Node
}

$node->attrGroups = $attributeGroups;
$this->completeExtraUseImports($attributeGroups);

return $node;
}
Expand Down Expand Up @@ -186,4 +195,21 @@ private function matchAnnotationToAttribute(

return null;
}

/**
* @param AttributeGroup[] $attributeGroups
*/
private function completeExtraUseImports(array $attributeGroups): void
{
foreach ($attributeGroups as $attributeGroup) {
foreach ($attributeGroup->attrs as $attr) {
$namespacedAttrName = $attr->name->getAttribute(AttributeKey::EXTRA_USE_IMPORT);
if (! is_string($namespacedAttrName)) {
continue;
}

$this->useNodesToAddCollector->addUseImport(new FullyQualifiedObjectType($namespacedAttrName));
}
}
}
}
33 changes: 33 additions & 0 deletions rules/Php80/ValueObject/AnnotationPropertyToAttributeClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Rector\Php80\ValueObject;

use Rector\Core\Validation\RectorAssert;

final class AnnotationPropertyToAttributeClass
{
public function __construct(
private readonly string $attributeClass,
private readonly string|int|null $annotationProperty = null,
private readonly bool $doesNeedNewImport = false
) {
RectorAssert::className($attributeClass);
}

public function getAnnotationProperty(): string|int|null
{
return $this->annotationProperty;
}

public function getAttributeClass(): string
{
return $this->attributeClass;
}

public function doesNeedNewImport(): bool
{
return $this->doesNeedNewImport;
}
}
31 changes: 23 additions & 8 deletions rules/Php80/ValueObject/NestedAnnotationToAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,35 @@

use Rector\Core\Validation\RectorAssert;
use Rector\Php80\Contract\ValueObject\AnnotationToAttributeInterface;
use Webmozart\Assert\Assert;

final class NestedAnnotationToAttribute implements AnnotationToAttributeInterface
{
/**
* @param array<string, string>|string[] $annotationPropertiesToAttributeClasses
* @var AnnotationPropertyToAttributeClass[]
*/
private array $annotationPropertiesToAttributeClasses = [];

/**
* @param array<string, string>|string[]|AnnotationPropertyToAttributeClass[] $annotationPropertiesToAttributeClasses
*/
public function __construct(
private readonly string $tag,
private readonly array $annotationPropertiesToAttributeClasses,
private readonly bool $removeOriginal = false
array $annotationPropertiesToAttributeClasses,
private readonly bool $removeOriginal = false,
) {
RectorAssert::className($tag);
Assert::allString($annotationPropertiesToAttributeClasses);

// back compatibility for raw scalar values
foreach ($annotationPropertiesToAttributeClasses as $annotationProperty => $attributeClass) {
if ($attributeClass instanceof AnnotationPropertyToAttributeClass) {
$this->annotationPropertiesToAttributeClasses[] = $attributeClass;
} else {
$this->annotationPropertiesToAttributeClasses[] = new AnnotationPropertyToAttributeClass(
$attributeClass,
$annotationProperty,
);
}
}
}

public function getTag(): string
Expand All @@ -28,7 +43,7 @@ public function getTag(): string
}

/**
* @return array<string, string>|string[]
* @return AnnotationPropertyToAttributeClass[]
*/
public function getAnnotationPropertiesToAttributeClasses(): array
{
Expand All @@ -47,8 +62,8 @@ public function shouldRemoveOriginal(): bool

public function hasExplicitParameters(): bool
{
foreach (array_keys($this->annotationPropertiesToAttributeClasses) as $itemName) {
if (is_string($itemName)) {
foreach ($this->annotationPropertiesToAttributeClasses as $annotationPropertyToAttributeClass) {
if (is_string($annotationPropertyToAttributeClass->getAnnotationProperty())) {
return true;
}
}
Expand Down

0 comments on commit f4f758c

Please sign in to comment.