mirror of
https://github.com/rectorphp/rector.git
synced 2024-07-04 08:33:33 +00:00
init uuid migration
This commit is contained in:
parent
697655d090
commit
c1303c6ba7
|
@ -1 +1,3 @@
|
|||
services:
|
||||
Rector\Doctrine\Rector\Class_\AddUuidToEntityWhereMissingRector: ~
|
||||
Rector\Doctrine\Rector\Class_\AddUuidMirrorForRelationPropertyRector: ~
|
||||
|
|
1
ecs.yaml
1
ecs.yaml
|
@ -109,6 +109,7 @@ parameters:
|
|||
|
||||
Symplify\CodingStandard\Sniffs\CleanCode\CognitiveComplexitySniff:
|
||||
# tough logic
|
||||
- 'packages/DoctrinePhpDocParser/src/Ast/PhpDoc/*/*TagValueNode.php'
|
||||
- 'packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/FqnNamePhpDocNodeDecorator.php'
|
||||
- 'packages/NodeTypeResolver/src/PHPStan/Type/StaticTypeAnalyzer.php'
|
||||
- 'src/NodeContainer/ParsedNodesByType.php'
|
||||
|
|
|
@ -15,12 +15,13 @@ use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
|
|||
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Class_\EntityTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\IdTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\JoinColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ManyToManyTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ManyToOneTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\OneToManyTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\OneToOneTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\RelationTagValueNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface;
|
||||
|
||||
final class PhpDocInfo
|
||||
{
|
||||
|
@ -175,6 +176,11 @@ final class PhpDocInfo
|
|||
return $this->getResolvedTypesAttribute($varTagValue);
|
||||
}
|
||||
|
||||
public function getDoctrineIdTagValueNode(): ?IdTagValueNode
|
||||
{
|
||||
return $this->matchChildValueNodeOfType(IdTagValueNode::class);
|
||||
}
|
||||
|
||||
public function getDoctrineManyToManyTagValueNode(): ?ManyToManyTagValueNode
|
||||
{
|
||||
return $this->matchChildValueNodeOfType(ManyToManyTagValueNode::class);
|
||||
|
@ -249,7 +255,7 @@ final class PhpDocInfo
|
|||
return $this->getResolvedTypesAttribute($returnTypeValueNode);
|
||||
}
|
||||
|
||||
public function getRelationTagValueNode(): ?RelationTagValueNodeInterface
|
||||
public function getDoctrineRelationTagValueNode(): ?DoctrineRelationTagValueNodeInterface
|
||||
{
|
||||
return $this->getDoctrineManyToManyTagValueNode() ??
|
||||
$this->getDoctrineOneToManyTagValueNode() ??
|
||||
|
@ -257,6 +263,19 @@ final class PhpDocInfo
|
|||
$this->getDoctrineManyToOneTagValueNode() ?? null;
|
||||
}
|
||||
|
||||
public function removeTagValueNodeFromNode(PhpDocTagValueNode $phpDocTagValueNode): void
|
||||
{
|
||||
foreach ($this->phpDocNode->children as $key => $phpDocChildNode) {
|
||||
if ($phpDocChildNode instanceof PhpDocTagNode) {
|
||||
if ($phpDocChildNode->value !== $phpDocTagValueNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($this->phpDocNode->children[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode
|
||||
{
|
||||
$phpDocNode = $this->getPhpDocNode();
|
||||
|
|
|
@ -31,6 +31,12 @@ final class OriginalSpacingRestorer
|
|||
|
||||
// replace system whitespace by old ones, include \n*
|
||||
$nodeOutputParts = Strings::split($nodeOutput, '#\s+#');
|
||||
|
||||
// new nodes were probably added, skip them
|
||||
if (count($oldWhitespaces) < count($nodeOutputParts)) {
|
||||
return $nodeOutput;
|
||||
}
|
||||
|
||||
$hasAsterixMultiline = false;
|
||||
foreach ($nodeOutputParts as $key => $nodeOutputPart) {
|
||||
if (isset($oldWhitespaces[$key])) {
|
||||
|
|
|
@ -37,7 +37,7 @@ final class DoctrineEntityManipulator
|
|||
|
||||
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($property);
|
||||
|
||||
$relationTagValueNode = $phpDocInfo->getRelationTagValueNode();
|
||||
$relationTagValueNode = $phpDocInfo->getDoctrineRelationTagValueNode();
|
||||
if ($relationTagValueNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ final class DoctrineEntityManipulator
|
|||
}
|
||||
|
||||
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($property);
|
||||
$relationTagValueNode = $phpDocInfo->getRelationTagValueNode();
|
||||
$relationTagValueNode = $phpDocInfo->getDoctrineRelationTagValueNode();
|
||||
|
||||
$shouldUpdate = false;
|
||||
if ($relationTagValueNode instanceof MappedByNodeInterface) {
|
||||
|
@ -125,7 +125,7 @@ final class DoctrineEntityManipulator
|
|||
}
|
||||
|
||||
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($stmt);
|
||||
if ($phpDocInfo->getRelationTagValueNode() === null) {
|
||||
if ($phpDocInfo->getDoctrineRelationTagValueNode() === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ final class RemoveUnusedDoctrineEntityMethodAndPropertyRectorTest extends Abstra
|
|||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/remove_inversed_by.php.inc',
|
||||
__DIR__ . '/Fixture/remove_inversed_by_non_fqn.php.inc',
|
||||
// skip
|
||||
// skip
|
||||
__DIR__ . '/Fixture/skip_double_entity_call.php.inc',
|
||||
__DIR__ . '/Fixture/skip_id_and_system.php.inc',
|
||||
__DIR__ . '/Fixture/skip_trait_called_method.php.inc',
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
parameters:
|
||||
doctrine_uuid_generator_class: 'Ramsey\Uuid\Doctrine\UuidGenerator'
|
||||
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
|
|
33
packages/Doctrine/src/AbstarctRector/DoctrineTrait.php
Normal file
33
packages/Doctrine/src/AbstarctRector/DoctrineTrait.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\AbstarctRector;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface;
|
||||
use Rector\Rector\AbstractRector\DocBlockManipulatorTrait;
|
||||
|
||||
trait DoctrineTrait
|
||||
{
|
||||
use DocBlockManipulatorTrait;
|
||||
|
||||
protected function isDoctrineEntityClass(Class_ $class): bool
|
||||
{
|
||||
$classPhpDocInfo = $this->getPhpDocInfo($class);
|
||||
if ($classPhpDocInfo === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $classPhpDocInfo->getDoctrineEntityTag();
|
||||
}
|
||||
|
||||
protected function getDoctrineRelationTagValueNode(Property $property): ?DoctrineRelationTagValueNodeInterface
|
||||
{
|
||||
$propertyPhpDocInfo = $this->getPhpDocInfo($property);
|
||||
if ($propertyPhpDocInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $propertyPhpDocInfo->getDoctrineRelationTagValueNode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Collector;
|
||||
|
||||
final class EntitiesWithAddedUuidPropertyCollector
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $classes = [];
|
||||
|
||||
public function addClass(string $class): void
|
||||
{
|
||||
$this->classes[] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClasses(): array
|
||||
{
|
||||
return $this->classes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Extension;
|
||||
|
||||
use Rector\Contract\Extension\FinishingExtensionInterface;
|
||||
use Rector\Doctrine\Collector\EntitiesWithAddedUuidPropertyCollector;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
final class ReportEntitiesWithAddedUuidFinishExtension implements FinishingExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @var EntitiesWithAddedUuidPropertyCollector
|
||||
*/
|
||||
private $entitiesWithAddedUuidPropertyCollector;
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
private $symfonyStyle;
|
||||
|
||||
public function __construct(
|
||||
EntitiesWithAddedUuidPropertyCollector $entitiesWithAddedUuidPropertyCollector,
|
||||
SymfonyStyle $symfonyStyle
|
||||
) {
|
||||
$this->entitiesWithAddedUuidPropertyCollector = $entitiesWithAddedUuidPropertyCollector;
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$classes = $this->entitiesWithAddedUuidPropertyCollector->getClasses();
|
||||
|
||||
if (count($classes)) {
|
||||
$this->symfonyStyle->section('Entities with new nullable $uuid property');
|
||||
$this->symfonyStyle->listing($classes);
|
||||
}
|
||||
}
|
||||
}
|
184
packages/Doctrine/src/NodeFactory/EntityUuidNodeFactory.php
Normal file
184
packages/Doctrine/src/NodeFactory/EntityUuidNodeFactory.php
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\NodeFactory;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\Doctrine\ValueObject\DoctrineClass;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\CustomIdGeneratorTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\GeneratedValueTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\IdTagValueNode;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
|
||||
final class EntityUuidNodeFactory
|
||||
{
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
/**
|
||||
* @var BuilderFactory
|
||||
*/
|
||||
private $builderFactory;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $doctrineUuidGeneratorClass;
|
||||
|
||||
public function __construct(
|
||||
string $doctrineUuidGeneratorClass,
|
||||
DocBlockManipulator $docBlockManipulator,
|
||||
BuilderFactory $builderFactory
|
||||
) {
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
$this->builderFactory = $builderFactory;
|
||||
$this->doctrineUuidGeneratorClass = $doctrineUuidGeneratorClass;
|
||||
}
|
||||
|
||||
public function createTemporaryUuidProperty(): Property
|
||||
{
|
||||
$uuidProperty = $this->builderFactory->property('uuid')
|
||||
->makePrivate()
|
||||
->getNode();
|
||||
|
||||
$this->decoratePropertyWithUuidAnnotations($uuidProperty, true, false);
|
||||
|
||||
return $uuidProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates: "$this->uid = \Ramsey\Uuid\Uuid::uuid4();"
|
||||
*/
|
||||
public function createUuidPropertyDefaultValueAssign(): Expression
|
||||
{
|
||||
$thisUuidPropertyFetch = new PropertyFetch(new Variable('this'), 'uuid');
|
||||
$uuid4StaticCall = new StaticCall(new FullyQualified('Ramsey\Uuid\Uuid'), 'uuid4');
|
||||
|
||||
$assign = new Assign($thisUuidPropertyFetch, $uuid4StaticCall);
|
||||
|
||||
return new Expression($assign);
|
||||
}
|
||||
|
||||
public function createConstructorWithUuidPropertyDefaultValueAssign(): ClassMethod
|
||||
{
|
||||
$classMethodBuilder = $this->builderFactory->method('__construct');
|
||||
$classMethodBuilder->makePublic();
|
||||
|
||||
$assign = $this->createUuidPropertyDefaultValueAssign();
|
||||
$classMethodBuilder->addStmt($assign);
|
||||
|
||||
return $classMethodBuilder->getNode();
|
||||
}
|
||||
|
||||
private function clearVarAndOrmAnnotations(Node $node): void
|
||||
{
|
||||
$docComment = $node->getDocComment();
|
||||
if ($docComment === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$clearedDocCommentText = Strings::replace($docComment->getText(), '#^(\s+)\*(\s+)\@(var|ORM)(.*?)$#ms');
|
||||
|
||||
$node->setDocComment(new Doc($clearedDocCommentText));
|
||||
}
|
||||
|
||||
private function decoratePropertyWithUuidAnnotations(Property $property, bool $isNullable, bool $isId): void
|
||||
{
|
||||
$this->clearVarAndOrmAnnotations($property);
|
||||
$this->replaceIntSerializerTypeWithString($property);
|
||||
|
||||
// add @var
|
||||
$this->docBlockManipulator->addTag($property, $this->createVarTagUuidInterface());
|
||||
|
||||
if ($isId) {
|
||||
// add @ORM\Id
|
||||
$this->docBlockManipulator->addTag($property, $this->createIdTag());
|
||||
}
|
||||
|
||||
$this->docBlockManipulator->addTag($property, $this->createUuidColumnTag($isNullable));
|
||||
|
||||
if ($isId) {
|
||||
$this->docBlockManipulator->addTag($property, $this->createGeneratedValueTag());
|
||||
$this->docBlockManipulator->addTag($property, $this->createCustomIdGeneratorTag());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://github.com/ramsey/uuid-doctrine/issues/50#issuecomment-348123520.
|
||||
*/
|
||||
private function replaceIntSerializerTypeWithString(Node $node): void
|
||||
{
|
||||
$docComment = $node->getDocComment();
|
||||
if ($docComment === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stringTypeText = Strings::replace(
|
||||
$docComment->getText(),
|
||||
'#(\@Serializer\\\\Type\(")(int)("\))#',
|
||||
'$1string$3'
|
||||
);
|
||||
|
||||
$node->setDocComment(new Doc($stringTypeText));
|
||||
}
|
||||
|
||||
private function createVarTagUuidInterface(): PhpDocTagNode
|
||||
{
|
||||
$varTagValueNode = new VarTagValueNode(new IdentifierTypeNode(
|
||||
'\\' . DoctrineClass::RAMSEY_UUID_INTERFACE
|
||||
), '', '');
|
||||
|
||||
return new PhpDocTagNode('@var', $varTagValueNode);
|
||||
}
|
||||
|
||||
private function createIdTag(): PhpDocTagNode
|
||||
{
|
||||
return new PhpDocTagNode(IdTagValueNode::SHORT_NAME, new IdTagValueNode());
|
||||
}
|
||||
|
||||
private function createUuidColumnTag(bool $isNullable): PhpDocTagNode
|
||||
{
|
||||
$columnTagValueNode = new ColumnTagValueNode(
|
||||
null,
|
||||
'uuid_binary',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
$isNullable ? true : null
|
||||
);
|
||||
|
||||
return new PhpDocTagNode($columnTagValueNode::SHORT_NAME, $columnTagValueNode);
|
||||
}
|
||||
|
||||
private function createGeneratedValueTag(): PhpDocTagNode
|
||||
{
|
||||
return new PhpDocTagNode(GeneratedValueTagValueNode::SHORT_NAME, new GeneratedValueTagValueNode('CUSTOM'));
|
||||
}
|
||||
|
||||
private function createCustomIdGeneratorTag(): PhpDocTagNode
|
||||
{
|
||||
return new PhpDocTagNode(
|
||||
CustomIdGeneratorTagValueNode::SHORT_NAME,
|
||||
new CustomIdGeneratorTagValueNode($this->doctrineUuidGeneratorClass)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Rector\Class_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\PropertyProperty;
|
||||
use PhpParser\Node\VarLikeIdentifier;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\JoinColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\JoinTableTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineRelationTagValueNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToManyTagNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToOneTagNodeInterface;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://spaceflow.io/ for sponsoring this rule - visit them on https://github.com/SpaceFlow-app
|
||||
*/
|
||||
final class AddUuidMirrorForRelationPropertyRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
public function __construct(DocBlockManipulator $docBlockManipulator)
|
||||
{
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Adds $uuid property to entities, that already have $id with integer type.' .
|
||||
'Require for step-by-step migration from int to uuid.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isDoctrineEntityClass($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// traverse relations and see which of them have freshly added uuid on the other side
|
||||
foreach ($node->stmts as $classStmt) {
|
||||
if (! $classStmt instanceof Property) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this relation already is or has uuid property
|
||||
if ($this->isName($classStmt, '*Uuid')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$uuidPropertyName = $this->getName($classStmt) . 'Uuid';
|
||||
|
||||
if ($this->hasClassPropertyName($node, $uuidPropertyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$doctrineRelationTagValueNode = $this->getDoctrineRelationTagValueNode($classStmt);
|
||||
if ($doctrineRelationTagValueNode === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($doctrineRelationTagValueNode->getTargetEntity() === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$node->stmts[] = $this->createMirrorNullable($classStmt);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates duplicated property, that has "*uuidSuffix"
|
||||
* and nullable join column, so we cna complete them manually
|
||||
*/
|
||||
private function createMirrorNullable(Property $property): Property
|
||||
{
|
||||
$propertyWithUuid = clone $property;
|
||||
|
||||
// this is needed to keep old property name
|
||||
$this->updateDocComment($propertyWithUuid);
|
||||
|
||||
// name must be changed after the doc comment update, because the reflection annotation needed for update of doc comment
|
||||
// would miss non existing *Uuid property
|
||||
$uuidPropertyName = $this->getName($propertyWithUuid) . 'Uuid';
|
||||
$newPropertyProperty = new PropertyProperty(new VarLikeIdentifier($uuidPropertyName));
|
||||
$propertyWithUuid->props = [$newPropertyProperty];
|
||||
|
||||
return $propertyWithUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates unique many-to-many table name like: first_table_uuid_second_table_uuid
|
||||
*/
|
||||
private function createManyToManyUuidTableName(Property $property): string
|
||||
{
|
||||
/** @var string $currentClass */
|
||||
$currentClass = $property->getAttribute(AttributeKey::CLASS_NAME);
|
||||
$shortCurrentClass = Strings::after($currentClass, '\\', -1);
|
||||
|
||||
$targetClass = $this->resolveTargetClass($property);
|
||||
if ($targetClass === null) {
|
||||
throw new ShouldNotHappenException(__METHOD__);
|
||||
}
|
||||
|
||||
$shortTargetClass = Strings::after($targetClass, '\\', -1);
|
||||
|
||||
return strtolower($shortCurrentClass . '_uuid_' . $shortTargetClass . '_uuid');
|
||||
}
|
||||
|
||||
private function resolveTargetClass(Property $property): ?string
|
||||
{
|
||||
/** @var PhpDocInfo $phpDocInfo */
|
||||
$phpDocInfo = $this->getPhpDocInfo($property);
|
||||
|
||||
/** @var DoctrineRelationTagValueNodeInterface $relationTagValueNode */
|
||||
$relationTagValueNode = $phpDocInfo->getDoctrineRelationTagValueNode();
|
||||
|
||||
return $relationTagValueNode->getFqnTargetEntity();
|
||||
}
|
||||
|
||||
private function updateDocComment(Property $property): void
|
||||
{
|
||||
$propertyPhpDocInfo = $this->getPhpDocInfo($property);
|
||||
if ($propertyPhpDocInfo === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var DoctrineRelationTagValueNodeInterface $doctrineRelationTagValueNode */
|
||||
$doctrineRelationTagValueNode = $propertyPhpDocInfo->getDoctrineRelationTagValueNode();
|
||||
|
||||
if ($doctrineRelationTagValueNode instanceof ToManyTagNodeInterface) {
|
||||
$this->refactorToManyPropertyPhpDocInfo($propertyPhpDocInfo, $property);
|
||||
} elseif ($doctrineRelationTagValueNode instanceof ToOneTagNodeInterface) {
|
||||
$this->refactorToOnePropertyPhpDocInfo($propertyPhpDocInfo);
|
||||
}
|
||||
|
||||
$this->docBlockManipulator->updateNodeWithPhpDocInfo($property, $propertyPhpDocInfo);
|
||||
}
|
||||
|
||||
private function refactorToManyPropertyPhpDocInfo(PhpDocInfo $propertyPhpDocInfo, Property $property): void
|
||||
{
|
||||
$doctrineJoinColumnTagValueNode = $propertyPhpDocInfo->getDoctrineJoinColumnTagValueNode();
|
||||
|
||||
if ($doctrineJoinColumnTagValueNode) {
|
||||
// replace @ORM\JoinColumn with @ORM\JoinTable
|
||||
$propertyPhpDocInfo->removeTagValueNodeFromNode($doctrineJoinColumnTagValueNode);
|
||||
}
|
||||
|
||||
$propertyPhpDocInfo->getPhpDocNode()->children[] = $this->createJoinTableTagNode($property);
|
||||
}
|
||||
|
||||
private function refactorToOnePropertyPhpDocInfo(PhpDocInfo $propertyPhpDocInfo): void
|
||||
{
|
||||
$joinColumnTagValueNode = $propertyPhpDocInfo->getDoctrineJoinColumnTagValueNode();
|
||||
|
||||
if ($joinColumnTagValueNode) {
|
||||
$joinColumnTagValueNode->changeNullable(true);
|
||||
$joinColumnTagValueNode->changeReferencedColumnName('uuid');
|
||||
} else {
|
||||
$propertyPhpDocInfo->getPhpDocNode()->children[] = $this->createJoinColumnTagNode();
|
||||
}
|
||||
}
|
||||
|
||||
private function hasClassPropertyName(Class_ $node, string $uuidPropertyName): bool
|
||||
{
|
||||
foreach ($node->stmts as $stmt) {
|
||||
if (! $stmt instanceof Property) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->isName($stmt, $uuidPropertyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function createJoinTableTagNode(Property $property): PhpDocTagNode
|
||||
{
|
||||
$joinTableName = $this->createManyToManyUuidTableName($property);
|
||||
|
||||
$joinTableTagValueNode = new JoinTableTagValueNode($joinTableName, null, [
|
||||
new JoinColumnTagValueNode(null, 'uuid'),
|
||||
], [new JoinColumnTagValueNode(null, 'uuid')]);
|
||||
|
||||
return new AttributeAwarePhpDocTagNode(JoinTableTagValueNode::SHORT_NAME, $joinTableTagValueNode);
|
||||
}
|
||||
|
||||
private function createJoinColumnTagNode(): PhpDocTagNode
|
||||
{
|
||||
$joinColumnTagValueNode = new JoinColumnTagValueNode(null, 'uuid', null, false);
|
||||
|
||||
return new AttributeAwarePhpDocTagNode(JoinColumnTagValueNode::SHORT_NAME, $joinColumnTagValueNode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Rector\Class_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Doctrine\Collector\EntitiesWithAddedUuidPropertyCollector;
|
||||
use Rector\Doctrine\NodeFactory\EntityUuidNodeFactory;
|
||||
use Rector\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://spaceflow.io/ for sponsoring this rule - visit them on https://github.com/SpaceFlow-app
|
||||
*/
|
||||
final class AddUuidToEntityWhereMissingRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var EntityUuidNodeFactory
|
||||
*/
|
||||
private $entityUuidNodeFactory;
|
||||
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
*/
|
||||
private $classManipulator;
|
||||
|
||||
/**
|
||||
* @var EntitiesWithAddedUuidPropertyCollector
|
||||
*/
|
||||
private $entitiesWithAddedUuidPropertyCollector;
|
||||
|
||||
public function __construct(
|
||||
EntityUuidNodeFactory $entityUuidNodeFactory,
|
||||
ClassManipulator $classManipulator,
|
||||
EntitiesWithAddedUuidPropertyCollector $entitiesWithAddedUuidPropertyCollector
|
||||
) {
|
||||
$this->entityUuidNodeFactory = $entityUuidNodeFactory;
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->entitiesWithAddedUuidPropertyCollector = $entitiesWithAddedUuidPropertyCollector;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Adds $uuid property to entities, that already have $id with integer type.' .
|
||||
'Require for step-by-step migration from int to uuid. ' .
|
||||
'In following step it should be renamed to $id and replace it'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isDoctrineEntityClass($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already has $uuid property
|
||||
if ($this->classManipulator->getProperty($node, 'uuid')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->hasClassIdPropertyWithUuidType($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. add to start of the class, so it can be easily seen
|
||||
$uuidProperty = $this->entityUuidNodeFactory->createTemporaryUuidProperty();
|
||||
$node->stmts = array_merge([$uuidProperty], $node->stmts);
|
||||
|
||||
// 2. add default value to uuid property
|
||||
$constructClassMethod = $node->getMethod('__construct');
|
||||
if ($constructClassMethod) {
|
||||
$assignExpression = $this->entityUuidNodeFactory->createUuidPropertyDefaultValueAssign();
|
||||
$constructClassMethod->stmts = array_merge([$assignExpression], (array) $constructClassMethod->stmts);
|
||||
} else {
|
||||
$constructClassMethod = $this->entityUuidNodeFactory->createConstructorWithUuidPropertyDefaultValueAssign();
|
||||
$node->stmts = array_merge([$constructClassMethod], $node->stmts);
|
||||
}
|
||||
|
||||
/** @var string $class */
|
||||
$class = $this->getName($node);
|
||||
$this->entitiesWithAddedUuidPropertyCollector->addClass($class);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function hasClassIdPropertyWithUuidType(Class_ $class): bool
|
||||
{
|
||||
foreach ($class->stmts as $classStmt) {
|
||||
if (! $classStmt instanceof Property) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->isName($classStmt, 'id')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$propertyPhpDocInfo = $this->getPhpDocInfo($classStmt);
|
||||
if ($propertyPhpDocInfo === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$idTagValueNode = $propertyPhpDocInfo->getDoctrineIdTagValueNode();
|
||||
if ($idTagValueNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get column!
|
||||
$columnTagValueNode = $propertyPhpDocInfo->getDoctrineColumnTagValueNode();
|
||||
if ($columnTagValueNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) Strings::match((string) $columnTagValueNode->getType(), '#^uuid(_binary)?$#');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,11 @@ namespace Rector\Doctrine\ValueObject;
|
|||
|
||||
final class DoctrineClass
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const RAMSEY_UUID_INTERFACE = 'Ramsey\Uuid\UuidInterface';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector;
|
||||
|
||||
use Rector\Doctrine\Rector\Class_\AddUuidMirrorForRelationPropertyRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class AddUuidToEntityWhereMissingRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/skip_already_added.php.inc',
|
||||
__DIR__ . '/Fixture/to_many.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return AddUuidMirrorForRelationPropertyRector::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SomeEntity
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private $amenity;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SomeEntity
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private $amenity;
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
* @ORM\JoinColumn(nullable=true, referencedColumnName="uuid")
|
||||
*/
|
||||
private $amenityUuid;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SkipAlreadyAdded
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private $amenity;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
* @ORM\JoinColumn(nullable=true, referencedColumnName="uuid")
|
||||
*/
|
||||
private $amenityUuid;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class ToMany
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
*/
|
||||
private $amenity;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class ToMany
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source\AnotherEntity", cascade={"persist", "merge"})
|
||||
*/
|
||||
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")})
|
||||
*/
|
||||
private $amenityUuid;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,25 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidMirrorForRelationPropertyRector\Source;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class AnotherEntity
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var \Ramsey\Uuid\UuidInterface
|
||||
* @ORM\Column (type="uuid_binary", unique=true, nullable=true)
|
||||
*/
|
||||
private $uuid;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector;
|
||||
|
||||
use Rector\Doctrine\Rector\Class_\AddUuidToEntityWhereMissingRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class AddUuidToEntityWhereMissingRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/already_has_constructor.php.inc',
|
||||
__DIR__ . '/Fixture/process_string_id.php.inc',
|
||||
// skip
|
||||
__DIR__ . '/Fixture/skip_id_with_uuid_type.php.inc',
|
||||
__DIR__ . '/Fixture/skip_id_with_uuid_binary_type.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return AddUuidToEntityWhereMissingRector::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class AlreadyHasConstructor
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->id = 5;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class AlreadyHasConstructor
|
||||
{
|
||||
/**
|
||||
* @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;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->uuid = \Ramsey\Uuid\Uuid::uuid4();
|
||||
$this->id = 5;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SomeEntityWithIntegerId
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SomeEntityWithIntegerId
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class ProcessStringId
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="string")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class ProcessStringId
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->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="string")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SkipIdWithUuidBinaryType
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="uuid_binary")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Doctrine\Tests\Rector\Class_\AddUuidToEntityWhereMissingRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SkipIdWithUuidType
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="uuid")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Array_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
/**
|
||||
* Helpers class for ordering items in values objects on call.
|
||||
* Beware of static methods as they might doom you on the edge of life.
|
||||
|
@ -9,17 +11,17 @@ namespace Rector\DoctrinePhpDocParser\Array_;
|
|||
final class ArrayItemStaticHelper
|
||||
{
|
||||
/**
|
||||
* @param string[] $items
|
||||
* @return string[]
|
||||
*/
|
||||
public static function removeItemFromArray(array $items, string $itemToRemove): array
|
||||
public static function resolveAnnotationItemsOrder(string $content): array
|
||||
{
|
||||
$itemPosition = array_search($itemToRemove, $items, true);
|
||||
if ($itemPosition !== null) {
|
||||
unset($items[$itemPosition]);
|
||||
$itemsOrder = [];
|
||||
$matches = Strings::matchAll($content, '#(?<item>\w+)=#m');
|
||||
foreach ($matches as $match) {
|
||||
$itemsOrder[] = $match['item'];
|
||||
}
|
||||
|
||||
return $items;
|
||||
return $itemsOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,9 +15,9 @@ abstract class AbstractDoctrineTagValueNode implements AttributeAwareNodeInterfa
|
|||
use AttributeTrait;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @var string[]|null
|
||||
*/
|
||||
protected $orderedVisibleItems = [];
|
||||
protected $orderedVisibleItems;
|
||||
|
||||
/**
|
||||
* @param mixed[] $item
|
||||
|
@ -36,7 +36,10 @@ abstract class AbstractDoctrineTagValueNode implements AttributeAwareNodeInterfa
|
|||
*/
|
||||
protected function printContentItems(array $contentItems): string
|
||||
{
|
||||
$contentItems = ArrayItemStaticHelper::filterAndSortVisibleItems($contentItems, $this->orderedVisibleItems);
|
||||
if ($this->orderedVisibleItems !== null) {
|
||||
$contentItems = ArrayItemStaticHelper::filterAndSortVisibleItems($contentItems, $this->orderedVisibleItems);
|
||||
}
|
||||
|
||||
if ($contentItems === []) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -30,7 +30,10 @@ final class EntityTagValueNode extends AbstractDoctrineTagValueNode
|
|||
{
|
||||
$contentItems = [];
|
||||
|
||||
$contentItems['repositoryClass'] = sprintf('repositoryClass="%s"', $this->repositoryClass);
|
||||
if ($this->repositoryClass !== null) {
|
||||
$contentItems['repositoryClass'] = sprintf('repositoryClass="%s"', $this->repositoryClass);
|
||||
}
|
||||
|
||||
$contentItems['readOnly'] = sprintf('readOnly=%s', $this->readOnly ? 'true' : 'false');
|
||||
|
||||
return $this->printContentItems($contentItems);
|
||||
|
@ -38,11 +41,6 @@ final class EntityTagValueNode extends AbstractDoctrineTagValueNode
|
|||
|
||||
public function removeRepositoryClass(): void
|
||||
{
|
||||
$itemPosition = array_search('repositoryClass', $this->orderedVisibleItems, true);
|
||||
if ($itemPosition !== null) {
|
||||
unset($this->orderedVisibleItems[$itemPosition]);
|
||||
}
|
||||
|
||||
$this->repositoryClass = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,16 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Nette\Utils\Json;
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
|
||||
final class ColumnTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\Column';
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
|
@ -19,34 +23,34 @@ final class ColumnTagValueNode extends AbstractDoctrineTagValueNode
|
|||
private $type;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $length;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @var int|null
|
||||
*/
|
||||
private $precision;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @var int|null
|
||||
*/
|
||||
private $scale;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var bool|null
|
||||
*/
|
||||
private $unique = false;
|
||||
private $unique;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var bool|null
|
||||
*/
|
||||
private $nullable = false;
|
||||
private $nullable;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
* @var mixed[]|null
|
||||
*/
|
||||
private $options = [];
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
|
@ -56,20 +60,19 @@ final class ColumnTagValueNode extends AbstractDoctrineTagValueNode
|
|||
/**
|
||||
* @param mixed[] $options
|
||||
* @param mixed $type
|
||||
* @param mixed $length
|
||||
* @param string[] $orderedVisibleItems
|
||||
* @param mixed|null $length
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name,
|
||||
$type,
|
||||
$length,
|
||||
int $precision,
|
||||
int $scale,
|
||||
bool $unique,
|
||||
bool $nullable,
|
||||
array $options,
|
||||
?string $columnDefinition,
|
||||
array $orderedVisibleItems
|
||||
?int $precision = null,
|
||||
?int $scale = null,
|
||||
?bool $unique = null,
|
||||
?bool $nullable = null,
|
||||
?array $options = null,
|
||||
?string $columnDefinition = null,
|
||||
?string $originalContent = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
|
@ -80,42 +83,64 @@ final class ColumnTagValueNode extends AbstractDoctrineTagValueNode
|
|||
$this->nullable = $nullable;
|
||||
$this->options = $options;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
$this->orderedVisibleItems = $orderedVisibleItems;
|
||||
|
||||
if ($originalContent !== null) {
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::resolveAnnotationItemsOrder($originalContent);
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$contentItems = [];
|
||||
|
||||
// required
|
||||
$contentItems['type'] = sprintf('type="%s"', $this->type);
|
||||
$contentItems['name'] = sprintf('name="%s"', $this->name);
|
||||
$contentItems['length'] = sprintf('length=%s', $this->length);
|
||||
$contentItems['precision'] = sprintf('precision=%s', $this->precision);
|
||||
$contentItems['scale'] = sprintf('scale=%s', $this->scale);
|
||||
$contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
|
||||
if ($this->options !== []) {
|
||||
$optionsContent = Json::encode($this->options);
|
||||
$optionsContent = Strings::replace($optionsContent, '#,#', ', ');
|
||||
$contentItems['options'] = 'options=' . $optionsContent;
|
||||
if ($this->type !== null) {
|
||||
$contentItems['type'] = sprintf('type="%s"', $this->type);
|
||||
}
|
||||
|
||||
$contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
|
||||
if ($this->name !== null) {
|
||||
$contentItems['name'] = sprintf('name="%s"', $this->name);
|
||||
}
|
||||
|
||||
if ($this->length !== null) {
|
||||
$contentItems['length'] = sprintf('length=%s', $this->length);
|
||||
}
|
||||
|
||||
if ($this->precision !== null) {
|
||||
$contentItems['precision'] = sprintf('precision=%s', $this->precision);
|
||||
}
|
||||
|
||||
if ($this->scale !== null) {
|
||||
$contentItems['scale'] = sprintf('scale=%s', $this->scale);
|
||||
}
|
||||
|
||||
if ($this->unique !== null) {
|
||||
$contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
|
||||
}
|
||||
|
||||
if ($this->nullable !== null) {
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
}
|
||||
|
||||
if ($this->options) {
|
||||
$contentItems['options'] = $this->printArrayItem($this->options, 'options');
|
||||
}
|
||||
|
||||
if ($this->columnDefinition !== null) {
|
||||
$contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
|
||||
}
|
||||
|
||||
return $this->printContentItems($contentItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
public function isNullable(): ?bool
|
||||
{
|
||||
return $this->nullable;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
|
||||
final class CustomIdGeneratorTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\CustomIdGenerator';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $class;
|
||||
|
||||
public function __construct(string $class)
|
||||
{
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return sprintf('(class="%s")', $this->class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
|
||||
final class GeneratedValueTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\GeneratedValue';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $strategy;
|
||||
|
||||
public function __construct(string $strategy)
|
||||
{
|
||||
$this->strategy = $strategy;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return sprintf('(stragety="%s")', $this->strategy);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
|
||||
final class IdTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\Id';
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -2,14 +2,20 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
|
||||
final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
* @var string
|
||||
*/
|
||||
private $nullable = false;
|
||||
public const SHORT_NAME = '@ORM\JoinColumn';
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private $nullable;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
|
@ -17,14 +23,14 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode
|
|||
private $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
private $referencedColumnName;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @var bool|null
|
||||
*/
|
||||
private $unique = false;
|
||||
private $unique;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
|
@ -41,20 +47,16 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode
|
|||
*/
|
||||
private $fieldName;
|
||||
|
||||
/**
|
||||
* @param string[] $orderedVisibleItems
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name,
|
||||
string $referencedColumnName,
|
||||
bool $unique,
|
||||
bool $nullable,
|
||||
?string $onDelete,
|
||||
?string $columnDefinition,
|
||||
?string $fieldName,
|
||||
array $orderedVisibleItems
|
||||
?bool $unique = null,
|
||||
?bool $nullable = null,
|
||||
?string $onDelete = null,
|
||||
?string $columnDefinition = null,
|
||||
?string $fieldName = null,
|
||||
?string $originalContent = null
|
||||
) {
|
||||
$this->orderedVisibleItems = $orderedVisibleItems;
|
||||
$this->nullable = $nullable;
|
||||
$this->name = $name;
|
||||
$this->referencedColumnName = $referencedColumnName;
|
||||
|
@ -62,26 +64,63 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode
|
|||
$this->onDelete = $onDelete;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
$this->fieldName = $fieldName;
|
||||
|
||||
if ($originalContent !== null) {
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::resolveAnnotationItemsOrder($originalContent);
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$contentItems = [];
|
||||
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
if ($this->nullable !== null) {
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
}
|
||||
|
||||
$contentItems['name'] = sprintf('name="%s"', $this->name);
|
||||
$contentItems['referencedColumnName'] = sprintf('referencedColumnName="%s"', $this->referencedColumnName);
|
||||
$contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
$contentItems['onDelete'] = sprintf('onDelete="%s"', $this->onDelete);
|
||||
$contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
|
||||
$contentItems['fieldName'] = sprintf('fieldName="%s"', $this->fieldName);
|
||||
if ($this->name !== null) {
|
||||
$contentItems['name'] = sprintf('name="%s"', $this->name);
|
||||
}
|
||||
|
||||
if ($this->referencedColumnName !== null) {
|
||||
$contentItems['referencedColumnName'] = sprintf('referencedColumnName="%s"', $this->referencedColumnName);
|
||||
}
|
||||
|
||||
if ($this->unique !== null) {
|
||||
$contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
|
||||
}
|
||||
|
||||
if ($this->nullable !== null) {
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
}
|
||||
|
||||
if ($this->onDelete !== null) {
|
||||
$contentItems['onDelete'] = sprintf('onDelete="%s"', $this->onDelete);
|
||||
}
|
||||
|
||||
if ($this->columnDefinition !== null) {
|
||||
$contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
|
||||
}
|
||||
|
||||
if ($this->fieldName !== null) {
|
||||
$contentItems['fieldName'] = sprintf('fieldName="%s"', $this->fieldName);
|
||||
}
|
||||
|
||||
return $this->printContentItems($contentItems);
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
public function changeNullable(bool $nullable): void
|
||||
{
|
||||
$this->nullable = $nullable;
|
||||
}
|
||||
|
||||
public function changeReferencedColumnName(string $referencedColumnName): void
|
||||
{
|
||||
$this->orderedVisibleItems[] = 'referencedColumnName';
|
||||
$this->referencedColumnName = $referencedColumnName;
|
||||
}
|
||||
|
||||
public function isNullable(): ?bool
|
||||
{
|
||||
return $this->nullable;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
|||
|
||||
final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\JoinTable';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -17,14 +22,14 @@ final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode
|
|||
private $schema;
|
||||
|
||||
/**
|
||||
* @var JoinColumnTagValueNode[]
|
||||
* @var JoinColumnTagValueNode[]|null
|
||||
*/
|
||||
private $joinColumns = [];
|
||||
private $joinColumns;
|
||||
|
||||
/**
|
||||
* @var JoinColumnTagValueNode[]
|
||||
* @var JoinColumnTagValueNode[]|null
|
||||
*/
|
||||
private $inverseJoinColumns = [];
|
||||
private $inverseJoinColumns;
|
||||
|
||||
/**
|
||||
* @param string[] $orderedVisibleItems
|
||||
|
@ -33,10 +38,10 @@ final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode
|
|||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
?string $schema,
|
||||
array $joinColumns,
|
||||
array $inverseJoinColumns,
|
||||
array $orderedVisibleItems
|
||||
?string $schema = null,
|
||||
?array $joinColumns = null,
|
||||
?array $inverseJoinColumns = null,
|
||||
?array $orderedVisibleItems = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->schema = $schema;
|
||||
|
@ -50,17 +55,23 @@ final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode
|
|||
$contentItems = [];
|
||||
|
||||
$contentItems['name'] = sprintf('name="%s"', $this->name);
|
||||
$contentItems['schema'] = sprintf('schema="%s"', $this->schema);
|
||||
|
||||
if ($this->schema !== null) {
|
||||
$contentItems['schema'] = sprintf('schema="%s"', $this->schema);
|
||||
}
|
||||
|
||||
if ($this->joinColumns) {
|
||||
$joinColumnsAsString = $this->printTagValueNodesSeparatedByComma($this->joinColumns, '@ORM\JoinColumn');
|
||||
$joinColumnsAsString = $this->printTagValueNodesSeparatedByComma(
|
||||
$this->joinColumns,
|
||||
JoinColumnTagValueNode::SHORT_NAME
|
||||
);
|
||||
$contentItems['joinColumns'] = sprintf('joinColumns={%s}', $joinColumnsAsString);
|
||||
}
|
||||
|
||||
if ($this->inverseJoinColumns) {
|
||||
$inverseJoinColumnsAsString = $this->printTagValueNodesSeparatedByComma(
|
||||
$this->inverseJoinColumns,
|
||||
'@ORM\JoinColumn'
|
||||
JoinColumnTagValueNode::SHORT_NAME
|
||||
);
|
||||
$contentItems['inverseJoinColumns'] = sprintf('inverseJoinColumns={%s}', $inverseJoinColumnsAsString);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\InversedByNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\MappedByNodeInterface;
|
||||
|
@ -10,6 +9,11 @@ use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToManyTagNodeInterface;
|
|||
|
||||
final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implements ToManyTagNodeInterface, MappedByNodeInterface, InversedByNodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\ManyToMany';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -80,8 +84,14 @@ final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implemen
|
|||
$contentItems = [];
|
||||
|
||||
$contentItems['targetEntity'] = sprintf('targetEntity="%s"', $this->targetEntity);
|
||||
$contentItems['mappedBy'] = sprintf('mappedBy="%s"', $this->mappedBy);
|
||||
$contentItems['inversedBy'] = sprintf('inversedBy="%s"', $this->inversedBy);
|
||||
|
||||
if ($this->mappedBy !== null) {
|
||||
$contentItems['mappedBy'] = sprintf('mappedBy="%s"', $this->mappedBy);
|
||||
}
|
||||
|
||||
if ($this->inversedBy !== null) {
|
||||
$contentItems['inversedBy'] = sprintf('inversedBy="%s"', $this->inversedBy);
|
||||
}
|
||||
|
||||
if ($this->cascade) {
|
||||
$contentItems['cascade'] = $this->printArrayItem($this->cascade, 'cascade');
|
||||
|
@ -116,21 +126,11 @@ final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implemen
|
|||
|
||||
public function removeMappedBy(): void
|
||||
{
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::removeItemFromArray(
|
||||
$this->orderedVisibleItems,
|
||||
'mappedBy'
|
||||
);
|
||||
|
||||
$this->mappedBy = null;
|
||||
}
|
||||
|
||||
public function removeInversedBy(): void
|
||||
{
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::removeItemFromArray(
|
||||
$this->orderedVisibleItems,
|
||||
'inversedBy'
|
||||
);
|
||||
|
||||
$this->inversedBy = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\InversedByNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToOneTagNodeInterface;
|
||||
|
||||
final class ManyToOneTagValueNode extends AbstractDoctrineTagValueNode implements ToOneTagNodeInterface, InversedByNodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\ManyToOne';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -84,11 +88,6 @@ final class ManyToOneTagValueNode extends AbstractDoctrineTagValueNode implement
|
|||
|
||||
public function removeInversedBy(): void
|
||||
{
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::removeItemFromArray(
|
||||
$this->orderedVisibleItems,
|
||||
'inversedBy'
|
||||
);
|
||||
|
||||
$this->inversedBy = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\MappedByNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToManyTagNodeInterface;
|
||||
|
||||
final class OneToManyTagValueNode extends AbstractDoctrineTagValueNode implements ToManyTagNodeInterface, MappedByNodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\OneToMany';
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
|
@ -101,11 +105,6 @@ final class OneToManyTagValueNode extends AbstractDoctrineTagValueNode implement
|
|||
|
||||
public function removeMappedBy(): void
|
||||
{
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::removeItemFromArray(
|
||||
$this->orderedVisibleItems,
|
||||
'mappedBy'
|
||||
);
|
||||
|
||||
$this->mappedBy = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_;
|
||||
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\AbstractDoctrineTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\InversedByNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\MappedByNodeInterface;
|
||||
|
@ -10,6 +9,11 @@ use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\ToOneTagNodeInterface;
|
|||
|
||||
final class OneToOneTagValueNode extends AbstractDoctrineTagValueNode implements ToOneTagNodeInterface, MappedByNodeInterface, InversedByNodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SHORT_NAME = '@ORM\OneToOne';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -74,8 +78,14 @@ final class OneToOneTagValueNode extends AbstractDoctrineTagValueNode implements
|
|||
$contentItems = [];
|
||||
|
||||
$contentItems['targetEntity'] = sprintf('targetEntity="%s"', $this->targetEntity);
|
||||
$contentItems['mappedBy'] = sprintf('mappedBy="%s"', $this->mappedBy);
|
||||
$contentItems['inversedBy'] = sprintf('inversedBy="%s"', $this->inversedBy);
|
||||
|
||||
if ($this->mappedBy !== null) {
|
||||
$contentItems['mappedBy'] = sprintf('mappedBy="%s"', $this->mappedBy);
|
||||
}
|
||||
|
||||
if ($this->inversedBy !== null) {
|
||||
$contentItems['inversedBy'] = sprintf('inversedBy="%s"', $this->inversedBy);
|
||||
}
|
||||
|
||||
if ($this->cascade) {
|
||||
$contentItems['cascade'] = $this->printArrayItem($this->cascade, 'cascade');
|
||||
|
@ -109,21 +119,11 @@ final class OneToOneTagValueNode extends AbstractDoctrineTagValueNode implements
|
|||
|
||||
public function removeInversedBy(): void
|
||||
{
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::removeItemFromArray(
|
||||
$this->orderedVisibleItems,
|
||||
'inversedBy'
|
||||
);
|
||||
|
||||
$this->inversedBy = null;
|
||||
}
|
||||
|
||||
public function removeMappedBy(): void
|
||||
{
|
||||
$this->orderedVisibleItems = ArrayItemStaticHelper::removeItemFromArray(
|
||||
$this->orderedVisibleItems,
|
||||
'mappedBy'
|
||||
);
|
||||
|
||||
$this->mappedBy = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc;
|
||||
|
||||
interface RelationTagValueNodeInterface
|
||||
interface DoctrineRelationTagValueNodeInterface
|
||||
{
|
||||
public function getTargetEntity(): ?string;
|
||||
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc;
|
||||
|
||||
interface ToManyTagNodeInterface extends RelationTagValueNodeInterface
|
||||
interface ToManyTagNodeInterface extends DoctrineRelationTagValueNodeInterface
|
||||
{
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
namespace Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc;
|
||||
|
||||
interface ToOneTagNodeInterface extends RelationTagValueNodeInterface
|
||||
interface ToOneTagNodeInterface extends DoctrineRelationTagValueNodeInterface
|
||||
{
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use Rector\Configuration\CurrentNodeProvider;
|
|||
use Rector\DoctrinePhpDocParser\AnnotationReader\NodeAnnotationReader;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Class_\EntityTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\IdTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\JoinColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\JoinTableTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\Property_\ManyToManyTagValueNode;
|
||||
|
@ -100,34 +101,38 @@ final class OrmTagParser
|
|||
Property $property,
|
||||
string $annotationContent
|
||||
): ?DoctrineTagNodeInterface {
|
||||
if ($tag === '@ORM\Column') {
|
||||
if ($tag === ColumnTagValueNode::SHORT_NAME) {
|
||||
return $this->createColumnTagValueNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === '@ORM\JoinColumn') {
|
||||
if ($tag === JoinColumnTagValueNode::SHORT_NAME) {
|
||||
return $this->createJoinColumnTagValueNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === '@ORM\ManyToMany') {
|
||||
if ($tag === ManyToManyTagValueNode::SHORT_NAME) {
|
||||
return $this->createManyToManyTagValueNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === '@ORM\ManyToOne') {
|
||||
if ($tag === ManyToOneTagValueNode::SHORT_NAME) {
|
||||
return $this->createManyToOneTagValueNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === '@ORM\OneToOne') {
|
||||
if ($tag === OneToOneTagValueNode::SHORT_NAME) {
|
||||
return $this->createOneToOneTagValueNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === '@ORM\OneToMany') {
|
||||
if ($tag === OneToManyTagValueNode::SHORT_NAME) {
|
||||
return $this->createOneToManyTagValueNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === '@ORM\JoinTable') {
|
||||
if ($tag === JoinTableTagValueNode::SHORT_NAME) {
|
||||
return $this->createJoinTableTagValeNode($property, $annotationContent);
|
||||
}
|
||||
|
||||
if ($tag === IdTagValueNode::SHORT_NAME) {
|
||||
return $this->createIdTagValueNode();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -156,7 +161,7 @@ final class OrmTagParser
|
|||
$column->nullable,
|
||||
$column->options,
|
||||
$column->columnDefinition,
|
||||
$this->resolveAnnotationItemsOrder($annotationContent)
|
||||
$annotationContent
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -319,7 +324,7 @@ final class OrmTagParser
|
|||
$joinColumn->onDelete,
|
||||
$joinColumn->columnDefinition,
|
||||
$joinColumn->fieldName,
|
||||
$this->resolveAnnotationItemsOrder($annotationContent)
|
||||
$annotationContent
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -327,4 +332,9 @@ final class OrmTagParser
|
|||
{
|
||||
return Strings::replace($annotationContent, '#(\s+)\*(\s+)#m', '$1$3');
|
||||
}
|
||||
|
||||
private function createIdTagValueNode(): IdTagValueNode
|
||||
{
|
||||
return new IdTagValueNode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -572,7 +572,7 @@ final class DocBlockManipulator
|
|||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
|
||||
$relationTagValueNode = $phpDocInfo->getRelationTagValueNode();
|
||||
$relationTagValueNode = $phpDocInfo->getDoctrineRelationTagValueNode();
|
||||
if ($relationTagValueNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ final class DoctrineRelationPropertyTypeInferer implements PropertyTypeInfererIn
|
|||
}
|
||||
|
||||
$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($property);
|
||||
$relationTagValueNode = $phpDocInfo->getRelationTagValueNode();
|
||||
$relationTagValueNode = $phpDocInfo->getDoctrineRelationTagValueNode();
|
||||
if ($relationTagValueNode === null) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ namespace Rector\Rector\AbstractRector;
|
|||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\Doctrine\AbstarctRector\DoctrineTrait;
|
||||
|
||||
trait AbstractRectorTrait
|
||||
{
|
||||
use AppliedRectorCollectorTrait;
|
||||
use DocBlockManipulatorTrait;
|
||||
use DoctrineTrait;
|
||||
use NodeTypeResolverTrait;
|
||||
use NameResolverTrait;
|
||||
use ConstFetchAnalyzerTrait;
|
||||
|
|
Loading…
Reference in New Issue
Block a user