Renaming Doctrine Annotation alias #8195
-
Hi everyone On the projects I work on, Previous team member decided to not follow the convention and didn't aliased the Doctrine Mapping folder (or used another alias that use Doctrine\ORM\Mapping;
class MyClass
{
/**
* @Mapping\column
*/
$property;
} And it seems the Doctrine set to change annotation to attributes only work with ORM alias (at least the @mapping annotation doesn't seem to be changed in the dry-run detected changes) So I wanted to make a rule to an alias to my use (if not already existing) and rename I tried making a rule for properties only (just to test) and looping through the Something like this (I don't have the code I made right now as It's on the work pc) foreach($phpDocNode->children as $key $child) {
...
$child->name = str_replace('@Mapping', '@ORM', $child->name);
}
...
return $node;
... I also tried unsetting the And of course i can't use the How could I rename the aliases using Rector ? ps : As I said I can't show you the code I did right now, so I hope it's clear enough. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 13 replies
-
I managed to do what I wanted with the following code (lots of hardcoded things as it's still in testing) : class RenmeMappingAnnotationRector extends AbstractRector
{
public function __construct(
private readonly UseImportsResolver $useImportsResolver,
private readonly DocBlockUpdater $docBlockUpdater,
) {
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition("", []);
}
public function getNodeTypes(): array
{
return [Node\Stmt\Property::class, Node\Stmt\Class_::class];
}
public function refactor(Node $node)
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
if ($phpDocInfo === null) {
return null;
}
$this->checkUseImportsAndImportORMIfNeeded($phpDocInfo);
$hasDocBlockChanged = $this->replaceAnnotation(
$phpDocInfo,
"@Mapping\\",
"@ORM\\"
);
if ($hasDocBlockChanged) {
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
$hasChanged = true;
}
if (!$hasChanged) {
return null;
}
return $node;
}
private function checkUseImportsAndImportORMIfNeeded(PhpDocInfo $phpDocInfo): void
{
$uses = $this->useImportsResolver->resolveBareUses();
$aliasAlreadyImported = false;
foreach ($uses as $keyUse => $use) {
if (empty($use->uses)) {
continue;
}
foreach ($use->uses as $keyUseUse => $useUse) {
if ($useUse->name->toString() !== "Doctrine\\ORM\\Mapping") {
continue;
}
if ($useUse->alias !== null && $useUse->alias->toString() === "ORM") {
//ORM alias already exists
$aliasAlreadyImported = true;
break 2;
}
}
}
foreach ($uses as $keyUse => $use) {
if (empty($use->uses)) {
continue;
}
$useUseHasChanged = false;
$useUseRemoved = false;
foreach ($use->uses as $keyUseUse => $useUse) {
if ($useUse->name->toString() !== "Doctrine\\ORM\\Mapping") {
continue;
}
if ($useUse->alias !== null && $useUse->alias->toString() === "ORM") {
//ORM alias already exists
continue;
}
//found Mapping namespace not aliased import
//we remove it
unset($use->uses[$keyUseUse]);
if ($aliasAlreadyImported) {
$useUseRemoved = true;
continue;
}
$useUseHasChanged = true;
//if not already imported we add the alias
$useUse->alias = new Node\Identifier("ORM");
$use->uses[$keyUseUse] = $useUse;
ksort($use->uses);
$use->uses = array_values($use->uses);
}
if ($useUseHasChanged || $useUseRemoved) {
if ($use->uses === []) {
unset($uses[$keyUse]);
} else {
$uses[$keyUse] = $use;
}
ksort($uses);
$uses = array_values($uses);
}
}
}
private function replaceAnnotation(PhpDocInfo $phpDocInfo, string $oldTag, string $newTag): bool
{
$hasChanged = false;
$phpDocNode = $phpDocInfo->getPhpDocNode();
foreach ($phpDocNode->children as $key => $phpDocChildNode) {
if (!$phpDocChildNode instanceof PhpDocTagNode) {
continue;
}
if (!$phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) {
continue;
}
if (!str_contains($phpDocChildNode->name, $oldTag)) {
continue;
}
unset($phpDocNode->children[$key]);
$newValue = $phpDocChildNode->value;
$newValue->identifierTypeNode = new IdentifierTypeNode(
str_replace("@Mapping\\", "@ORM\\", $newValue->identifierTypeNode->name)
);
$phpDocNode->children[$key] = new SpacelessPhpDocTagNode(
str_replace("@Mapping\\", "@ORM\\", $phpDocChildNode->name),
$newValue
);
//sort by key to avoid moving annotation too much
ksort($phpDocNode->children);
$phpDocNode->children = array_values($phpDocNode->children);
$hasChanged = true;
}
return $hasChanged;
}
} But I have a problem the removal of use statement. With my code in -use Doctrine\ORM\Mapping;
+use ; I checked the Is there any known fix to remove use statements ? |
Beta Was this translation helpful? Give feedback.
-
Hi, removing use statements should on top level node First, include then, verify after it can be removed: |
Beta Was this translation helpful? Give feedback.
-
Seems like it doesn't work that much... On nested Annotation it does weird things - *
- * @Mapping\Table(
- * name="entity",
- * uniqueConstraints={@UniqueConstraint(name="access_unique", columns={"column1", "column2"})}
- * )
- * @Mapping\Entity(repositoryClass="path\to\Repository")
- * @Mapping\HasLifecycleCallbacks()
- */
+ *
+ * @ORM\Table(name="entity", uniqueConstraints={(name="access_unique", columns={"column1", "column2"})})
+ * @ORM\Entity(repositoryClass="path\to\Repository")
+ * @ORM\HasLifecycleCallbacks()
+ */ I guess it's because I only create a new |
Beta Was this translation helpful? Give feedback.
-
Sorry to add more to this discussion ! I had to update my private function replaceAnnotation(PhpDocInfo $phpDocInfo, string $oldTag, string $newTag): bool
{
$hasChanged = false;
$phpDocNode = $phpDocInfo->getPhpDocNode();
foreach ($phpDocNode->children as $key => $phpDocChildNode) {
$phpDocChildNode->setAttribute('start_and_end',null);
if (!$phpDocChildNode instanceof SpacelessPhpDocTagNode) {
continue;
}
if (!$phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) {
continue;
}
if (!str_contains($phpDocChildNode->name, $oldTag)
|| !str_contains($phpDocChildNode->value->identifierTypeNode->name, $oldTag)
) {
continue;
}
$phpDocChildNode->name = str_replace($oldTag, $newTag, $phpDocChildNode->name);
$phpDocChildNode->value->identifierTypeNode->name = str_replace(
$oldTag,
$newTag,
$phpDocChildNode->value->identifierTypeNode->name
);
//trigger change
$phpDocChildNode->setAttribute('start_and_end',null);
$phpDocNode->children[$key] = $phpDocChildNode;
//sort by key to avoid moving annotation too much
ksort($phpDocNode->children);
$phpDocNode->children = array_values($phpDocNode->children);
$hasChanged = true;
}
return $hasChanged;
} I had to put the Without that, if the annotation I want to rename are NOT at the end, the old one will comme back if ($startAndEnd instanceof StartAndEnd && !$shouldReprintChildNode) {
$isLastToken = $nodeCount === $key;
// correct previously changed node
$this->correctPreviouslyReprintedFirstNode($key, $startAndEnd);
$output = $this->addTokensFromTo($output, $this->currentTokenPosition, $startAndEnd->getEnd(), $isLastToken);
if (str_contains($output, '@Mapping')) {
//dd($this->currentTokenPosition, $startAndEnd->getEnd(), $isLastToken);
}
$this->currentTokenPosition = $startAndEnd->getEnd();
return \rtrim($output);
} in Here is the diff I had before my change in /**
* Class CommandExport
* @package App\Domain\Tenant\Export
- *
+ *
+ * @ORM\Table(name="table")
+ * @ORM\Entity(repositoryClass="tableRepo")
* @Mapping\Table(name="table")
* @Mapping\Entity(repositoryClass="tableRepo")
*
* @ref test
- */
+ */ As you can see, the old node changed from Is there a better way to trigger a change than changing the StartEnd attribute ? |
Beta Was this translation helpful? Give feedback.
Hi, removing use statements should on top level node
FileWithoutNamespace
orNamespace
.First, include
FileWithoutNamespace
andNamespace
on after target node ongetNodeTypes()
method:https://github.com/laminas/laminas-servicemanager-migration/blob/cdedb23f9df8480daead4dbef79b35f26fedcc86/src/Rector/Class_/ImplementsFactoryInterfaceToPsrFactoryRector.php#L64
then, verify after it can be removed:
https://github.com/laminas/laminas-servicemanager-migration/blob/1.42.x/src/Rector/Class_/ImplementsFactoryInterfaceToPsrFactoryRector.php#L72-L73