diff --git a/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php b/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php index 7fbb5a419214..3de233d7669d 100644 --- a/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php +++ b/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php @@ -21,6 +21,7 @@ use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ManyToOneTagValueNode; use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\OneToManyTagValueNode; use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\OneToOneTagValueNode; +use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\TableTagValueNode; use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface; final class PhpDocInfo @@ -181,6 +182,11 @@ public function getDoctrineIdTagValueNode(): ?IdTagValueNode return $this->matchChildValueNodeOfType(IdTagValueNode::class); } + public function getDoctrineTableTagValueNode(): ?TableTagValueNode + { + return $this->matchChildValueNodeOfType(TableTagValueNode::class); + } + public function getDoctrineManyToManyTagValueNode(): ?ManyToManyTagValueNode { return $this->matchChildValueNodeOfType(ManyToManyTagValueNode::class); diff --git a/packages/Doctrine/src/AbstarctRector/DoctrineTrait.php b/packages/Doctrine/src/AbstarctRector/DoctrineTrait.php index 1b06d4bd6bc1..d2d02ec40bb4 100644 --- a/packages/Doctrine/src/AbstarctRector/DoctrineTrait.php +++ b/packages/Doctrine/src/AbstarctRector/DoctrineTrait.php @@ -5,6 +5,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; use Rector\Doctrine\PhpDocParser\DoctrineDocBlockResolver; +use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface; trait DoctrineTrait { @@ -26,8 +27,32 @@ protected function isDoctrineEntityClass(Class_ $class): bool return $this->doctrineDocBlockResolver->isDoctrineEntityClass($class); } + protected function isDoctrineEntityClassWithIdProperty(Class_ $class): bool + { + if (! $this->doctrineDocBlockResolver->isDoctrineEntityClass($class)) { + return false; + } + + foreach ($class->stmts as $classStmt) { + if (! $classStmt instanceof Property) { + continue; + } + + if ($this->doctrineDocBlockResolver->hasPropertyDoctrineIdTag($classStmt)) { + return true; + } + } + + return false; + } + protected function getTargetEntity(Property $property): ?string { return $this->doctrineDocBlockResolver->getTargetEntity($property); } + + protected function getDoctrineRelationTagValueNode(Property $property): ?DoctrineRelationTagValueNodeInterface + { + return $this->doctrineDocBlockResolver->getDoctrineRelationTagValueNode($property); + } } diff --git a/packages/Doctrine/src/Collector/EntitiesWithAddedUuidPropertyCollector.php b/packages/Doctrine/src/Collector/EntitiesWithAddedUuidPropertyCollector.php deleted file mode 100644 index b896e38d6e85..000000000000 --- a/packages/Doctrine/src/Collector/EntitiesWithAddedUuidPropertyCollector.php +++ /dev/null @@ -1,24 +0,0 @@ -classes[] = $class; - } - - /** - * @return string[] - */ - public function getClasses(): array - { - return $this->classes; - } -} diff --git a/packages/Doctrine/src/Collector/UuidMigrationDataCollector.php b/packages/Doctrine/src/Collector/UuidMigrationDataCollector.php new file mode 100644 index 000000000000..f2fb1138636e --- /dev/null +++ b/packages/Doctrine/src/Collector/UuidMigrationDataCollector.php @@ -0,0 +1,37 @@ +propertiesByClass[$class]['properties'][] = $property; + } + + public function addClassToManyRelationProperty(string $class, string $property, string $tableName): void + { + $this->propertiesByClass[$class]['to_many_relations'][] = [ + 'property' => $property, + 'table_name' => $tableName, + ]; + } + + public function addClassToOneRelationProperty(string $class, string $property): void + { + $this->propertiesByClass[$class]['to_one_relations'][] = $property; + } + + /** + * @return string[][][] + */ + public function getPropertiesByClass(): array + { + return $this->propertiesByClass; + } +} diff --git a/packages/Doctrine/src/Extension/ReportEntitiesWithAddedPropertiesFinishExtension.php b/packages/Doctrine/src/Extension/ReportEntitiesWithAddedPropertiesFinishExtension.php new file mode 100644 index 000000000000..3e8081a07a10 --- /dev/null +++ b/packages/Doctrine/src/Extension/ReportEntitiesWithAddedPropertiesFinishExtension.php @@ -0,0 +1,46 @@ +uuidMigrationDataCollector = $uuidMigrationDataCollector; + $this->symfonyStyle = $symfonyStyle; + } + + public function run(): void + { + $propertiesByClass = $this->uuidMigrationDataCollector->getPropertiesByClass(); + if ($propertiesByClass === []) { + return; + } + + $data = [ + 'title' => 'Entities with new properties', + 'added_properties_by_class' => $propertiesByClass, + ]; + + $jsonContent = Json::encode($data, Json::PRETTY); + + $this->symfonyStyle->writeln($jsonContent); + } +} diff --git a/packages/Doctrine/src/Extension/ReportEntitiesWithAddedUuidFinishExtension.php b/packages/Doctrine/src/Extension/ReportEntitiesWithAddedUuidFinishExtension.php deleted file mode 100644 index 3cd672d87be4..000000000000 --- a/packages/Doctrine/src/Extension/ReportEntitiesWithAddedUuidFinishExtension.php +++ /dev/null @@ -1,40 +0,0 @@ -entitiesWithAddedUuidPropertyCollector = $entitiesWithAddedUuidPropertyCollector; - $this->symfonyStyle = $symfonyStyle; - } - - public function run(): void - { - $classes = $this->entitiesWithAddedUuidPropertyCollector->getClasses(); - - if ($classes === []) { - return; - } - - $this->symfonyStyle->section('Entities with new nullable $uuid property'); - $this->symfonyStyle->listing($classes); - } -} diff --git a/packages/Doctrine/src/PhpDocParser/DoctrineDocBlockResolver.php b/packages/Doctrine/src/PhpDocParser/DoctrineDocBlockResolver.php index 4cfcbf91bdf7..0a56ceae3350 100644 --- a/packages/Doctrine/src/PhpDocParser/DoctrineDocBlockResolver.php +++ b/packages/Doctrine/src/PhpDocParser/DoctrineDocBlockResolver.php @@ -6,6 +6,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\TableTagValueNode; use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface; use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator; @@ -41,6 +42,16 @@ public function getTargetEntity(Property $property): ?string return $doctrineRelationTagValueNode->getTargetEntity(); } + public function hasPropertyDoctrineIdTag(Property $property): bool + { + $propertyPhpDocInfo = $this->getPhpDocInfo($property); + if ($propertyPhpDocInfo === null) { + return false; + } + + return (bool) $propertyPhpDocInfo->getDoctrineIdTagValueNode(); + } + public function getDoctrineRelationTagValueNode(Property $property): ?DoctrineRelationTagValueNodeInterface { $propertyPhpDocInfo = $this->getPhpDocInfo($property); @@ -51,6 +62,16 @@ public function getDoctrineRelationTagValueNode(Property $property): ?DoctrineRe return $propertyPhpDocInfo->getDoctrineRelationTagValueNode(); } + public function getDoctrineTableTagValueNode(Class_ $class): ?TableTagValueNode + { + $classPhpDocInfo = $this->getPhpDocInfo($class); + if ($classPhpDocInfo === null) { + return null; + } + + return $classPhpDocInfo->getDoctrineTableTagValueNode(); + } + private function getPhpDocInfo(Node $node): ?PhpDocInfo { if ($node->getDocComment() === null) { diff --git a/packages/Doctrine/src/Rector/Class_/AddUuidMirrorForRelationPropertyRector.php b/packages/Doctrine/src/Rector/Class_/AddUuidMirrorForRelationPropertyRector.php index f4583a2e0b53..9718cab0b210 100644 --- a/packages/Doctrine/src/Rector/Class_/AddUuidMirrorForRelationPropertyRector.php +++ b/packages/Doctrine/src/Rector/Class_/AddUuidMirrorForRelationPropertyRector.php @@ -10,10 +10,13 @@ use PhpParser\Node\Stmt\PropertyProperty; use PhpParser\Node\VarLikeIdentifier; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\Doctrine\Collector\UuidMigrationDataCollector; use Rector\Doctrine\PhpDocParser\Ast\PhpDoc\PhpDocTagNodeFactory; +use Rector\Doctrine\Uuid\UuidTableNameResolver; use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface; use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToManyTagNodeInterface; use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToOneTagNodeInterface; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator; use Rector\Rector\AbstractRector; use Rector\RectorDefinition\RectorDefinition; @@ -33,12 +36,26 @@ final class AddUuidMirrorForRelationPropertyRector extends AbstractRector */ private $phpDocTagNodeFactory; + /** + * @var UuidMigrationDataCollector + */ + private $uuidMigrationDataCollector; + + /** + * @var UuidTableNameResolver + */ + private $uuidTableNameResolver; + public function __construct( DocBlockManipulator $docBlockManipulator, - PhpDocTagNodeFactory $phpDocTagNodeFactory + PhpDocTagNodeFactory $phpDocTagNodeFactory, + UuidMigrationDataCollector $uuidMigrationDataCollector, + UuidTableNameResolver $uuidTableNameResolver ) { $this->docBlockManipulator = $docBlockManipulator; $this->phpDocTagNodeFactory = $phpDocTagNodeFactory; + $this->uuidMigrationDataCollector = $uuidMigrationDataCollector; + $this->uuidTableNameResolver = $uuidTableNameResolver; } public function getDefinition(): RectorDefinition @@ -99,18 +116,18 @@ private function createMirrorNullable(Property $property): Property $newPropertyProperty = new PropertyProperty(new VarLikeIdentifier($uuidPropertyName)); $propertyWithUuid->props = [$newPropertyProperty]; + $this->addNewPropertyToCollector($property, $uuidPropertyName); + return $propertyWithUuid; } private function updateDocComment(Property $property): void { + /** @var PhpDocInfo $propertyPhpDocInfo */ $propertyPhpDocInfo = $this->getPhpDocInfo($property); - if ($propertyPhpDocInfo === null) { - return; - } /** @var DoctrineRelationTagValueNodeInterface $doctrineRelationTagValueNode */ - $doctrineRelationTagValueNode = $propertyPhpDocInfo->getDoctrineRelationTagValueNode(); + $doctrineRelationTagValueNode = $this->getDoctrineRelationTagValueNode($property); if ($doctrineRelationTagValueNode instanceof ToManyTagNodeInterface) { $this->refactorToManyPropertyPhpDocInfo($propertyPhpDocInfo, $property); @@ -130,9 +147,8 @@ private function refactorToManyPropertyPhpDocInfo(PhpDocInfo $propertyPhpDocInfo $propertyPhpDocInfo->removeTagValueNodeFromNode($doctrineJoinColumnTagValueNode); } - $propertyPhpDocInfo->getPhpDocNode()->children[] = $this->phpDocTagNodeFactory->createJoinTableTagNode( - $property - ); + $joinTableTagNode = $this->phpDocTagNodeFactory->createJoinTableTagNode($property); + $propertyPhpDocInfo->getPhpDocNode()->children[] = $joinTableTagNode; } private function refactorToOnePropertyPhpDocInfo(PhpDocInfo $propertyPhpDocInfo): void @@ -189,4 +205,25 @@ private function shouldSkipProperty(Class_ $class, Property $property): bool return false; } + + private function addNewPropertyToCollector(Property $property, string $propertyName): void + { + /** @var string $className */ + $className = $property->getAttribute(AttributeKey::CLASS_NAME); + + /** @var DoctrineRelationTagValueNodeInterface $doctrineRelationTagValueNode */ + $doctrineRelationTagValueNode = $this->getDoctrineRelationTagValueNode($property); + + $joinTableName = $this->uuidTableNameResolver->resolveManyToManyTableNameForProperty($property); + + if ($doctrineRelationTagValueNode instanceof ToManyTagNodeInterface) { + $this->uuidMigrationDataCollector->addClassToManyRelationProperty( + $className, + $propertyName, + $joinTableName + ); + } elseif ($doctrineRelationTagValueNode instanceof ToOneTagNodeInterface) { + $this->uuidMigrationDataCollector->addClassToOneRelationProperty($className, $propertyName); + } + } } diff --git a/packages/Doctrine/src/Rector/Class_/AddUuidToEntityWhereMissingRector.php b/packages/Doctrine/src/Rector/Class_/AddUuidToEntityWhereMissingRector.php index e8d541fb2e2b..646fe8c51d01 100644 --- a/packages/Doctrine/src/Rector/Class_/AddUuidToEntityWhereMissingRector.php +++ b/packages/Doctrine/src/Rector/Class_/AddUuidToEntityWhereMissingRector.php @@ -8,7 +8,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; -use Rector\Doctrine\Collector\EntitiesWithAddedUuidPropertyCollector; +use Rector\Doctrine\Collector\UuidMigrationDataCollector; use Rector\Doctrine\NodeFactory\EntityUuidNodeFactory; use Rector\PhpParser\Node\Manipulator\ClassManipulator; use Rector\Rector\AbstractRector; @@ -30,18 +30,18 @@ final class AddUuidToEntityWhereMissingRector extends AbstractRector private $classManipulator; /** - * @var EntitiesWithAddedUuidPropertyCollector + * @var UuidMigrationDataCollector */ - private $entitiesWithAddedUuidPropertyCollector; + private $uuidMigrationDataCollector; public function __construct( EntityUuidNodeFactory $entityUuidNodeFactory, ClassManipulator $classManipulator, - EntitiesWithAddedUuidPropertyCollector $entitiesWithAddedUuidPropertyCollector + UuidMigrationDataCollector $uuidMigrationDataCollector ) { $this->entityUuidNodeFactory = $entityUuidNodeFactory; $this->classManipulator = $classManipulator; - $this->entitiesWithAddedUuidPropertyCollector = $entitiesWithAddedUuidPropertyCollector; + $this->uuidMigrationDataCollector = $uuidMigrationDataCollector; } public function getDefinition(): RectorDefinition @@ -66,7 +66,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (! $this->isDoctrineEntityClass($node)) { + if (! $this->isDoctrineEntityClassWithIdProperty($node)) { return null; } @@ -95,7 +95,7 @@ public function refactor(Node $node): ?Node /** @var string $class */ $class = $this->getName($node); - $this->entitiesWithAddedUuidPropertyCollector->addClass($class); + $this->uuidMigrationDataCollector->addClassAndProperty($class, 'uuid'); return $node; } diff --git a/packages/Doctrine/src/Uuid/UuidTableNameResolver.php b/packages/Doctrine/src/Uuid/UuidTableNameResolver.php index b9fcafb687c8..5bb3cef4e187 100644 --- a/packages/Doctrine/src/Uuid/UuidTableNameResolver.php +++ b/packages/Doctrine/src/Uuid/UuidTableNameResolver.php @@ -3,9 +3,11 @@ namespace Rector\Doctrine\Uuid; use Nette\Utils\Strings; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; use Rector\Doctrine\PhpDocParser\DoctrineDocBlockResolver; use Rector\Exception\ShouldNotHappenException; +use Rector\NodeContainer\ParsedNodesByType; use Rector\NodeTypeResolver\Node\AttributeKey; final class UuidTableNameResolver @@ -15,9 +17,17 @@ final class UuidTableNameResolver */ private $doctrineDocBlockResolver; - public function __construct(DoctrineDocBlockResolver $doctrineDocBlockResolver) - { + /** + * @var ParsedNodesByType + */ + private $parsedNodesByType; + + public function __construct( + DoctrineDocBlockResolver $doctrineDocBlockResolver, + ParsedNodesByType $parsedNodesByType + ) { $this->doctrineDocBlockResolver = $doctrineDocBlockResolver; + $this->parsedNodesByType = $parsedNodesByType; } /** @@ -25,18 +35,24 @@ public function __construct(DoctrineDocBlockResolver $doctrineDocBlockResolver) */ public function resolveManyToManyTableNameForProperty(Property $property): string { - /** @var string $currentClass */ - $currentClass = $property->getAttribute(AttributeKey::CLASS_NAME); - $shortCurrentClass = $this->resolveShortClassName($currentClass); + /** @var Class_ $currentClass */ + $currentClass = $property->getAttribute(AttributeKey::CLASS_NODE); + $currentTableName = $this->resolveTableNameFromClass($currentClass); $targetEntity = $this->doctrineDocBlockResolver->getTargetEntity($property); if ($targetEntity === null) { throw new ShouldNotHappenException(__METHOD__); } - $shortTargetEntity = $this->resolveShortClassName($targetEntity); + $targetEntityClass = $this->parsedNodesByType->findClass($targetEntity); + if ($targetEntityClass === null) { + // dummy fallback + $targetTableName = $this->resolveShortClassName($targetEntity); + } else { + $targetTableName = $this->resolveTableNameFromClass($targetEntityClass); + } - return strtolower($shortCurrentClass . '_uuid_' . $shortTargetEntity . '_uuid'); + return strtolower($currentTableName . '_' . $targetTableName . '_uuid'); } private function resolveShortClassName(string $currentClass): string @@ -47,4 +63,20 @@ private function resolveShortClassName(string $currentClass): string return (string) Strings::after($currentClass, '\\', -1); } + + private function resolveTableNameFromClass(Class_ $class): string + { + $tableTagValueNode = $this->doctrineDocBlockResolver->getDoctrineTableTagValueNode($class); + if ($tableTagValueNode !== null) { + $tableName = $tableTagValueNode->getName(); + if ($tableName !== null) { + return $tableName; + } + } + + /** @var string $className */ + $className = $class->getAttribute(AttributeKey::CLASS_NAME); + + return $this->resolveShortClassName($className); + } } diff --git a/packages/Doctrine/tests/Rector/Class_/AddUuidMirrorForRelationPropertyRector/Fixture/to_many.php.inc b/packages/Doctrine/tests/Rector/Class_/AddUuidMirrorForRelationPropertyRector/Fixture/to_many.php.inc index 5bd69ffde7f9..249c2065944a 100644 --- a/packages/Doctrine/tests/Rector/Class_/AddUuidMirrorForRelationPropertyRector/Fixture/to_many.php.inc +++ b/packages/Doctrine/tests/Rector/Class_/AddUuidMirrorForRelationPropertyRector/Fixture/to_many.php.inc @@ -6,6 +6,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity + * @ORM\Table(name="wohoo") */ class ToMany { @@ -25,6 +26,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity + * @ORM\Table(name="wohoo") */ class ToMany { @@ -34,7 +36,7 @@ class ToMany private $amenity; /** * @ORM\ManyToMany(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"}) - * @ORM\JoinTable (name="tomany_uuid_anotherentity_uuid", joinColumns={@ORM\JoinColumn(referencedColumnName="uuid")}, inverseJoinColumns={@ORM\JoinColumn(referencedColumnName="uuid")}) + * @ORM\JoinTable (name="wohoo_anotherentity_uuid", joinColumns={@ORM\JoinColumn(referencedColumnName="uuid")}, inverseJoinColumns={@ORM\JoinColumn(referencedColumnName="uuid")}) */ private $amenityUuid; } diff --git a/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/AddUuidToEntityWhereMissingRectorTest.php b/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/AddUuidToEntityWhereMissingRectorTest.php index bf1179967a99..4393ce603d36 100644 --- a/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/AddUuidToEntityWhereMissingRectorTest.php +++ b/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/AddUuidToEntityWhereMissingRectorTest.php @@ -14,7 +14,9 @@ public function test(): void __DIR__ . '/Fixture/already_has_constructor.php.inc', __DIR__ . '/Fixture/process_string_id.php.inc', __DIR__ . '/Fixture/with_parent_constructor.php.inc', - // skip + __DIR__ . '/Fixture/add_single_table_inheritance.php.inc', + __DIR__ . '/Fixture/add_single_table_inheritance_with_identifier.php.inc', + // skip __DIR__ . '/Fixture/skip_id_with_uuid_type.php.inc', __DIR__ . '/Fixture/skip_id_with_uuid_binary_type.php.inc', ]); diff --git a/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/Fixture/add_single_table_inheritance.php.inc b/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/Fixture/add_single_table_inheritance.php.inc new file mode 100644 index 000000000000..bb1efab4171a --- /dev/null +++ b/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/Fixture/add_single_table_inheritance.php.inc @@ -0,0 +1,68 @@ + +----- +uuid = \Ramsey\Uuid\Uuid::uuid4(); + } + /** + * @var \Ramsey\Uuid\UuidInterface + * @ORM\Column (type="uuid_binary", unique=true, nullable=true) + */ + private $uuid; + /** + * @var int + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; +} + +/** + * @ORM\Entity + */ +class AddSingleTableInheritance extends ParentSingleTableInheritance +{ +} + +?> diff --git a/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/Fixture/add_single_table_inheritance_with_identifier.php.inc b/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/Fixture/add_single_table_inheritance_with_identifier.php.inc new file mode 100644 index 000000000000..1ae4b921c1a0 --- /dev/null +++ b/packages/Doctrine/tests/Rector/Class_/AddUuidToEntityWhereMissingRector/Fixture/add_single_table_inheritance_with_identifier.php.inc @@ -0,0 +1,68 @@ + +----- +uuid = \Ramsey\Uuid\Uuid::uuid4(); + } + /** + * @var \Ramsey\Uuid\UuidInterface + * @ORM\Column (type="uuid_binary", unique=true, nullable=true) + */ + private $uuid; + /** + * @var int + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $identifier; +} + +/** + * @ORM\Entity + */ +class AddSingleTableInheritanceAgain extends ParentSingleTableInheritanceWithIdentifier +{ +} + +?> diff --git a/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Class_/EntityTagValueNode.php b/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Class_/EntityTagValueNode.php index d7f1f4ee54bd..72e728084c83 100644 --- a/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Class_/EntityTagValueNode.php +++ b/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Class_/EntityTagValueNode.php @@ -6,6 +6,11 @@ final class EntityTagValueNode extends AbstractDoctrineTagValueNode { + /** + * @var string + */ + public const SHORT_NAME = '@ORM\Entity'; + /** * @var string|null */ diff --git a/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Property_/TableTagValueNode.php b/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Property_/TableTagValueNode.php new file mode 100644 index 000000000000..7ef47507da80 --- /dev/null +++ b/packages/DoctrinePhpDocParser/src/Ast/PhpDoc/Property_/TableTagValueNode.php @@ -0,0 +1,93 @@ +name = $name; + $this->schema = $schema; + $this->indexes = $indexes; + $this->uniqueConstaints = $uniqueConstaints; + $this->options = $options; + + if ($originalContent !== null) { + $this->orderedVisibleItems = ArrayItemStaticHelper::resolveAnnotationItemsOrder($originalContent); + } + } + + public function __toString(): string + { + $contentItems = []; + + if ($this->name !== null) { + $contentItems['name'] = sprintf('name="%s"', $this->name); + } + + if ($this->schema !== null) { + $contentItems['schema'] = sprintf('schema="%s"', $this->schema); + } + + if ($this->indexes) { + $contentItems['indexes'] = $this->printArrayItem($this->indexes, 'indexes'); + } + + if ($this->uniqueConstaints) { + $contentItems['uniqueConstaints'] = $this->printArrayItem($this->uniqueConstaints, 'uniqueConstaints'); + } + + if ($this->options !== []) { + $contentItems['options'] = $this->printArrayItem($this->options, 'options'); + } + + return $this->printContentItems($contentItems); + } + + public function getName(): ?string + { + return $this->name; + } +} diff --git a/packages/DoctrinePhpDocParser/src/PhpDocParser/OrmTagParser.php b/packages/DoctrinePhpDocParser/src/PhpDocParser/OrmTagParser.php index c80c3bf412a8..90618f12c1b3 100644 --- a/packages/DoctrinePhpDocParser/src/PhpDocParser/OrmTagParser.php +++ b/packages/DoctrinePhpDocParser/src/PhpDocParser/OrmTagParser.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; +use Doctrine\ORM\Mapping\Table; use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Stmt\Class_; @@ -28,6 +29,7 @@ use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ManyToOneTagValueNode; use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\OneToManyTagValueNode; use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\OneToOneTagValueNode; +use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\TableTagValueNode; use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineTagNodeInterface; use Rector\NodeTypeResolver\Node\AttributeKey; @@ -80,9 +82,13 @@ public function parse(TokenIterator $tokenIterator, string $tag): ?PhpDocTagValu // Entity tags if ($node instanceof Class_) { - if ($tag === '@ORM\Entity') { + if ($tag === EntityTagValueNode::SHORT_NAME) { return $this->createEntityTagValueNode($node, $annotationContent); } + + if ($tag === TableTagValueNode::SHORT_NAME) { + return $this->createTableTagValueNode($node, $annotationContent); + } } // Property tags @@ -101,6 +107,10 @@ private function createPropertyTagValueNode( Property $property, string $annotationContent ): ?DoctrineTagNodeInterface { + if ($tag === IdTagValueNode::SHORT_NAME) { + return $this->createIdTagValueNode(); + } + if ($tag === ColumnTagValueNode::SHORT_NAME) { return $this->createColumnTagValueNode($property, $annotationContent); } @@ -129,10 +139,6 @@ private function createPropertyTagValueNode( return $this->createJoinTableTagValeNode($property, $annotationContent); } - if ($tag === IdTagValueNode::SHORT_NAME) { - return $this->createIdTagValueNode(); - } - return null; } @@ -146,6 +152,21 @@ private function createEntityTagValueNode(Class_ $node, string $annotationConten )); } + private function createTableTagValueNode(Class_ $node, string $annotationContent): TableTagValueNode + { + /** @var Table $table */ + $table = $this->nodeAnnotationReader->readDoctrineClassAnnotation($node, Table::class); + + return new TableTagValueNode( + $table->name, + $table->schema, + $table->indexes, + $table->uniqueConstraints, + $table->options, + $annotationContent + ); + } + private function createColumnTagValueNode(Property $property, string $annotationContent): ColumnTagValueNode { /** @var Column $column */