rectorphp / rector-doctrine Goto Github PK
View Code? Open in Web Editor NEWRector upgrade rules for Doctrine
Home Page: https://getrector.com
License: MIT License
Rector upgrade rules for Doctrine
Home Page: https://getrector.com
License: MIT License
If Table annotation was using only indexes and uniqueConstraints, then after a migration to attributes it will become empty.
And an empty Table attribute is useless.
Setting a default value to a negative integer where the entity property default type is integer throws a system error.
https://getrector.com/demo/9a115ab7-81a5-4624-a6c2-f24c89e4e845
[ERROR] Could not process "file.php" file, due to:
"System error: ""
Stack trace:
#0
vendor/rector/rector/vendor/rector/rector-doctrine/rules/CodeQuality/Rector/Property/CorrectDefaultTypesOnEntit
yPropertyRector.php(95):
Rector\Doctrine\CodeQuality\Rector\Property\CorrectDefaultTypesOnEntityPropertyRector->refactorToIntType(Objec
t(PhpParser\Node\Stmt\PropertyProperty), Object(PhpParser\Node\Stmt\Property))
#1 vendor/rector/rector/src/Rector/AbstractRector.php(200):
Rector\Doctrine\CodeQuality\Rector\Property\CorrectDefaultTypesOnEntityPropertyRector->refactor(Object(PhpPars
er\Node\Stmt\Property))
#2 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(176):
Rector\Core\Rector\AbstractRector->enterNode(Object(PhpParser\Node\Stmt\Property))
#3 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105):
PhpParser\NodeTraverser->traverseArray(Array)
#4 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196):
PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\Class_))
#5 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105):
PhpParser\NodeTraverser->traverseArray(Array)
#6 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196):
PhpParser\NodeTraverser->traverseNode(Object(PhpParser\Node\Stmt\Namespace_))
#7 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(85):
PhpParser\NodeTraverser->traverseArray(Array)
#8 vendor/rector/rector/src/PhpParser/NodeTraverser/RectorNodeTraverser.php(43):
PhpParser\NodeTraverser->traverse(Array)
#9 vendor/rector/rector/src/Application/FileProcessor.php(44):
Rector\Core\PhpParser\NodeTraverser\RectorNodeTraverser->traverse(Array)
#10 vendor/rector/rector/src/Application/FileProcessor/PhpFileProcessor.php(115):
Rector\Core\Application\FileProcessor->refactor(Object(Rector\Core\ValueObject\Application\File))
#11 vendor/rector/rector/packages/Parallel/WorkerRunner.php(136):
Rector\Core\Application\FileProcessor\PhpFileProcessor->process(Object(Rector\Core\ValueObject\Application\Fil
e), Object(Rector\Core\ValueObject\Configuration))
#12 vendor/rector/rector/packages/Parallel/WorkerRunner.php(107):
Rector\Parallel\WorkerRunner->processFile(Object(Rector\Core\ValueObject\Application\File),
Object(Rector\Core\ValueObject\Configuration), Array)
#13 vendor/rector/rector/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):
Rector\Parallel\WorkerRunner->Rector\Parallel\{closure}(Array)
#14 vendor/rector/rector/vendor/clue/ndjson-react/src/Decoder.php(117):
RectorPrefix202307\Evenement\EventEmitter->emit('data', Array)
#15 vendor/rector/rector/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):
RectorPrefix202307\Clue\React\NDJson\Decoder->handleData(Array)
#16 vendor/rector/rector/vendor/react/stream/src/Util.php(62):
RectorPrefix202307\Evenement\EventEmitter->emit('data', Array)
#17 vendor/rector/rector/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97):
RectorPrefix202307\React\Stream\Util::RectorPrefix202307\React\Stream\{closure}('{"action":"main...')
#18 vendor/rector/rector/vendor/react/stream/src/DuplexResourceStream.php(154):
RectorPrefix202307\Evenement\EventEmitter->emit('data', Array)
#19 vendor/rector/rector/vendor/react/event-loop/src/StreamSelectLoop.php(201):
RectorPrefix202307\React\Stream\DuplexResourceStream->handleData(Resource id #2544)
#20 vendor/rector/rector/vendor/react/event-loop/src/StreamSelectLoop.php(173):
RectorPrefix202307\React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)
#21 vendor/rector/rector/src/Console/Command/WorkerCommand.php(63):
RectorPrefix202307\React\EventLoop\StreamSelectLoop->run()
#22 vendor/rector/rector/vendor/symfony/console/Command/Command.php(325):
Rector\Core\Console\Command\WorkerCommand->execute(Object(RectorPrefix202307\Symfony\Component\Console\Input\A
rgvInput), Object(RectorPrefix202307\Symfony\Component\Console\Output\ConsoleOutput))
#23 vendor/rector/rector/vendor/symfony/console/Application.php(944):
RectorPrefix202307\Symfony\Component\Console\Command\Command->run(Object(RectorPrefix202307\Symfony\Component\
Console\Input\ArgvInput), Object(RectorPrefix202307\Symfony\Component\Console\Output\ConsoleOutput))
#24 vendor/rector/rector/vendor/symfony/console/Application.php(326):
RectorPrefix202307\Symfony\Component\Console\Application->doRunCommand(Object(Rector\Core\Console\Command\Work
erCommand), Object(RectorPrefix202307\Symfony\Component\Console\Input\ArgvInput),
Object(RectorPrefix202307\Symfony\Component\Console\Output\ConsoleOutput))
#25 vendor/rector/rector/src/Console/ConsoleApplication.php(54):
RectorPrefix202307\Symfony\Component\Console\Application->doRun(Object(RectorPrefix202307\Symfony\Component\Co
nsole\Input\ArgvInput), Object(RectorPrefix202307\Symfony\Component\Console\Output\ConsoleOutput))
#26 vendor/rector/rector/vendor/symfony/console/Application.php(212):
Rector\Core\Console\ConsoleApplication->doRun(Object(RectorPrefix202307\Symfony\Component\Console\Input\ArgvIn
put), Object(RectorPrefix202307\Symfony\Component\Console\Output\ConsoleOutput))
#27 vendor/rector/rector/bin/rector.php(132): RectorPrefix202307\Symfony\Component\Console\Application->run()
#28 vendor/rector/rector/bin/rector(4): require_once('v...')
#29 vendor/bin/rector(115): include('v...')
#30 {main}". On line: 136
According to the UPGRADE guide, classes constants related to Criteria::ASC
and Criteria::DESC
are deprecated, in favor of Doctrine\Common\Collection\Order
.
We can also use the rule Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector
for this (like in #294).
Can I also write a PR for this ?
This is in the package doctrine-collection: I think I will need to create a new ruleset and test suite, isn't it ?
Hi @TomasVotruba ,
There seems to be a typo in this rule : https://github.com/rectorphp/rector-doctrine/blob/main/docs/rector_rules_overview.md#improvedoctrinecollectiondoctypeinentityrector.
@var Collection<int, Training>|Trainer[]
-> @var Collection<int, Trainer>|Trainer[]
?
Thks
I ran AnnotationToAttribute set and got a weird thing happening
I had this before
/**
* @Mapping\ManyToOne(targetEntity=Application::class, inversedBy="options", fetch="EXTRA_LAZY", cascade={"persist"})
* @Mapping\JoinColumn(name="application_id", referencedColumnName="id", nullable=false)
And after rector I got this
#[ORM\ManyToOne(targetEntity: Application::class, inversedBy: 'options', fetch: 'EXTRA_LAZY', cascade: ['persist', new ORM\JoinColumn(name: 'application_id', referencedColumnName: 'id', nullable: false)])]
#[ORM\JoinColumn(name: 'application_id', referencedColumnName: 'id', nullable: false)]
The JoinColumn
was added to the cascade array and is causing the following exception
strtolower(): Argument #1 ($string) must be of type string, Doctrine\ORM\Mapping\JoinColumn given
According to the upgrade guide, class constants Doctrine\ORM\Query\Lexer::T_*
are now deprecated, in favor of Doctrine\ORM\Query\TokenType::T_*
.
Can we write a rector rule for this ?
When collection is initialized to ArrayCollection
with values, Rector seems to be adding a redundant initialization call.
Sometimes we want the constructor of a parent entity to accept an array of child entities for collection to be initialized with.
Test example #90
Problem
Adding the rule MoveRepositoryFromParentToConstructorRector
results in a ShouldNotHappenException
with the message An entity was not found for ...
.
Stacktrace
[file] module/App/src/Repository/TestRepository.php
[rule] Rector\Doctrine\Rector\Class_\MoveRepositoryFromParentToConstructorRector
PHP Fatal error: Uncaught Rector\Core\Exception\ShouldNotHappenException: An entity was not found for "App\Repository\TestRepository" repository. in /app/vendor/rector/rector/vendor/rector/rector-doctrine/src/NodeFactory/RepositoryAssignFactory.php:48
Stack trace:
#0 /app/vendor/rector/rector/vendor/rector/rector-doctrine/src/Rector/Class_/MoveRepositoryFromParentToConstructorRector.php(105): Rector\Doctrine\NodeFactory\RepositoryAssignFactory->create()
#1 /app/vendor/rector/rector/src/Rector/AbstractRector.php(209): Rector\Doctrine\Rector\Class_\MoveRepositoryFromParentToConstructorRector->refactor()
#2 /app/vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(176): Rector\Core\Rector\AbstractRector->enterNode()
#3 /app/vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(105): PhpParser\NodeTraverser->traverseArray()
#4 /app/vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(196): PhpParser\NodeTraverser->traverseNode()
#5 /app/vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(85): PhpParser\NodeTraverser->traverseArray()
#6 /app/vendor/rector/rector/src/PhpParser/NodeTraverser/RectorNodeTraverser.php(43): PhpParser\NodeTraverser->traverse()
#7 /app/vendor/rector/rector/src/Application/FileProcessor.php(44): Rector\Core\PhpParser\NodeTraverser\RectorNodeTraverser->traverse()
#8 /app/vendor/rector/rector/src/Application/FileProcessor/PhpFileProcessor.php(115): Rector\Core\Application\FileProcessor->refactor()
#9 /app/vendor/rector/rector/src/Application/ApplicationFileProcessor.php(162): Rector\Core\Application\FileProcessor\PhpFileProcessor->process()
#10 /app/vendor/rector/rector/src/Application/ApplicationFileProcessor.php(132): Rector\Core\Application\ApplicationFileProcessor->processFiles()
#11 /app/vendor/rector/rector/src/Console/Command/ProcessCommand.php(123): Rector\Core\Application\ApplicationFileProcessor->run()
#12 /app/vendor/rector/rector/vendor/symfony/console/Command/Command.php(325): Rector\Core\Console\Command\ProcessCommand->execute()
#13 /app/vendor/rector/rector/vendor/symfony/console/Application.php(944): RectorPrefix202306\Symfony\Component\Console\Command\Command->run()
#14 /app/vendor/rector/rector/vendor/symfony/console/Application.php(326): RectorPrefix202306\Symfony\Component\Console\Application->doRunCommand()
#15 /app/vendor/rector/rector/src/Console/ConsoleApplication.php(49): RectorPrefix202306\Symfony\Component\Console\Application->doRun()
#16 /app/vendor/rector/rector/vendor/symfony/console/Application.php(212): Rector\Core\Console\ConsoleApplication->doRun()
#17 /app/vendor/rector/rector/bin/rector.php(132): RectorPrefix202306\Symfony\Component\Console\Application->run()
#18 /app/vendor/rector/rector/bin/rector(5): require_once('...')
#19 /app/vendor/bin/rector(120): include('...')
#20 {main}
Reproduction
Can not get it reproduced on getrector/demo
, so I'll give an example.
rector.php
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Doctrine\Rector\Class_\MoveRepositoryFromParentToConstructorRector;
return static function (RectorConfig $rectorConfig): void
{
$rectorConfig->phpVersion(phpVersion: PhpVersion::PHP_82);
$rectorConfig->importNames();
$rectorConfig->importShortClasses();
$rectorConfig->disableParallel();
$rectorConfig->autoloadPaths(autoloadPaths: [
__DIR__ . '/vendor/autoload.php',
]);
$rectorConfig->paths(paths: [
__DIR__ . '/module/App/src/Repository/TestRepository.php',
]);
$rectorConfig->rules([
MoveRepositoryFromParentToConstructorRector::class,
]);
};
module/App/src/Repository/TestRepository.php
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\Test;
use Doctrine\ORM\EntityRepository;
final class TestRepository extends EntityRepository
{
public function findAllTest(): array {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t')
->from(Test::class, 't');
return $qb->getQuery()->getResult();
}
}
Run vendor/bin/rector process --clear-cache --debug
gives stacktrace above as result.
After looking at the resolveFromRepositoryClass
I added a method with a phpdoc having a return type.
module/App/src/Repository/TestRepository.php
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\Test;
use Doctrine\ORM\EntityRepository;
final class TestRepository extends EntityRepository
{
public function findAllTest(): array {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t')
->from(Test::class, 't');
return $qb->getQuery()->getResult();
}
/**
* @return Test
*/
public function findSingle(): Test {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t')
->from(Test::class, 't')
->setMaxResults(1);
return $qb->getQuery()->getSingleResult();
}
}
After running rector I get the following result:
module/App/src/Repository/TestRepository.php
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\Test;
use Doctrine\ORM\EntityRepository;
final class TestRepository
{
/**
* @var EntityRepository<Test>
*/
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em, ClassMetadata $class, EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(\Test::class);
parent::__construct($em, $class);
}
public function findAllTest(): array {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t')
->from(Test::class, 't');
return $qb->getQuery()->getResult();
}
/**
* @return Test
*/
public function findSingle(): Test {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t')
->from(Test::class, 't')
->setMaxResults(1);
return $qb->getQuery()->getSingleResult();
}
}
Now it created the constructor but it has atleast 3 issues and does not reflect the documentation:
EntityManagerInterface
in the constructor.ClassMetadata
in constructor, not stated in the docs.$entityManager->getRepository(\Test::class)
but instead $entityManager->getRepository(Test::class)
Conclusion
The example/explanation in the documentation does not reflect what is actually required for this rule to work. With the current multitude of issues it produces it currently does not seem to work correctly.
Package doctrine/orm
2.9 stable now supports PHP 8.0 attribute alternatives for Doctrine annotations.
See https://www.doctrine-project.org/2021/05/24/orm2.9.html
There is place for a first-time, who would like to chip-in little work to upgrade of every Doctrine project in the world.
doctrine-orm-29.php
AnnotationToAttributeRector
rule - https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md#annotationtoattributerectorORM\Entity
, it would look like this $services->set(AnnotationToAttributeRector::class)
->call('configure', [[
AnnotationToAttributeRector::ANNOTATION_TO_ATTRIBUTE => ValueObjectInliner::inline([
new AnnotationToAttribute(
'Doctrine\ORM\Mapping\Entity',
'Doctrine\ORM\Mapping\Entity'
),
]),
]]);
Would be nice, but is not needed.
Write a test so we have this behavior verified.
rector.php
with import('...-doctrine-orm-29.php')
set/Fixtures
directory to verifyPHP 8.1 introduced a way to handle nested attributes, something missing from the 8.0 implementation and needed for some Doctrine annotations.
Doctrine has the annotation, @ORM\JoinColumns
, used like so:
@ORM\JoinColumns({
@ORM\JoinColumn(name="thing_id", referencedColumnName="id", nullable=false)
})
Currently, the DoctrineAnnotationClassToAttributeRector
skips over this annotation, presumably due to PHP 8.0's lack of nested attribute support. Is there an update on supporting this or a plan in place?
Similar to rectorphp/rector-phpunit#114, rectorphp/rector-phpunit#114 / rectorphp/rector-phpunit#115
I would like to set the PHPDocInfo @var
based on doctrine metadata. Via phpstan-doctrine phpstan already knows the correct type from the Doctrine Metadata () which should be set to the property and getter of that property.
I'm not yet sure how I can access the expected PHPStan type. Any hints how to achieve this would be welcome.
Hi! I found some problems using rector to transform annotations to attributes:
@Index
must be moved out of @Table
(Table attribute was deleted as it's not necessary anymore)
same for unique constraints
(Table attribute was deleted as it's not necessary anymore)
@JoinTable
should be split
I had to do these modifications manually after the initial rector run โ these were not easy to find, partly because of silent BC by Doctrine.
I found some information about these in doctrine/orm#9334 (comment) and https://www.doctrine-project.org/projects/doctrine-orm/en/2.11/reference/attributes-reference.html
Hi,
I love rector and rector doctrine <3. But.
<?php
namespace App\Wex\BaseBundle\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait LinkedToAnyEntity
{
/**
* @ORM\Column(type="integer", nullable=true)
*/
private ?int $entityId = null;
}
vendor/bin/rector process -vvv src/Wex/BaseBundle/Entity/Traits/LinkedToAnyEntity.php
Result :
[applying] Rector\Doctrine\Rector\Property\CorrectDefaultTypesOnEntityPropertyRector
In CorrectDefaultTypesOnEntityPropertyRector.php line 116:
[Rector\Core\Exception\NotImplementedYetException]
Probably the same problem as rectorphp/rector#5454
And should be solved by rectorphp/rector#5974
Same issue with
\Rector\Doctrine\Rector\Property\CorrectDefaultTypesOnEntityPropertyRector::class
\Rector\Doctrine\Rector\Property\RemoveRedundantDefaultPropertyAnnotationValuesRector::class
Thanks
Rule definition: Replace EventSubscriberInterface with AsDoctrineListener attribute(s)
but only check for
if (!$this->hasImplements($node, 'Doctrine\\Common\\EventSubscriber')) {
return null;
}
while it could be EventSubscriberInterface
It would be great to have a rector to be able to replace string literals used in the type
argument of the Column
(and JoinColumn) attribute of doctrine/orm by the corresponding constants from
\Doctrine\DBAL\Types\Types` (when there is such a constant, as custom types are also supported)
The TypedPropertyFromColumnTypeRector exists, but as it seams it works only on Annotations not on Attributes.
We're getting the following deprecation warnings from Doctrine DBAL:
Passing an array for the first argument to QueryBuilder::select() is deprecated, pass each value as an individual variadic argument instead.
So for example
$queryBuilder->select(['field1', 'field2', 'field3', 'etc']);
should become
$queryBuilder->select('field1', 'field2', 'field3', 'etc');
I ma not sure if I understand it correctly.
But in this function, there should be default false, not true?
By default ORM\Column is nullable=false. If I don't have nullable neiehter in the attribute nor in phpdoc, it souhldn't be nullable for property. Am I missing something?
Example here also show ignoring nullable:false in the attribute, but it can be due to others refactoring rules.
use Doctrine\DBAL\Types\Types;
...
- #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
+ #[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE)]
Types
is imported but rector still tries to force usage FQN when using DoctrineSetList::DOCTRINE_CODE_QUALITY
.
Moved here from rectorphp/rector#7034
A @JoinTable-Annotation is added, even if it is already present.
Subject | Details |
---|---|
Rector version | last dev-master |
Installed as | composer dependency |
See https://getrector.org/demo/c057a4bc-9be7-4347-a4cd-f0d719e71c17
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table(name="test")
*/
class Test
{
/**
* @ORM\ManyToMany(targetEntity="Type", inversedBy="tests")
* @ORM\JoinTable(
* name="objects_portal_type",
* joinColumns={@ORM\JoinColumn(name="test_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="type_id", referencedColumnName="id")}
* )
* @var \Doctrine\Common\Collections\Collection<\Type>
*/
private \Doctrine\Common\Collections\Collection $types;
public function __construct()
{
$this->types = new ArrayCollection();
}
}
RemoveRedundantDefaultPropertyAnnotationValuesRector
Improve the existing annotation, but do not duplicate it
I have a doctrine definition like so:
/**
* @var int
*/
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer', name: 'some_id')]
protected $id;
running the code quality set list is changing it to:
/**
* @var int
*/
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, name: 'some_id')]
protected ?int $id = null;
I'd personally prefer Types::INTEGER
to be written without a qualifier, using an import instead. But I can let my IDE handle that after rector is done, so not a huge deal. The problem is that after I change the qualifier to an import and run the set list again, the qualifier is re-introduced. I'd expect rector to leave fields alone, if they already make use of the constant.
With doctrine/orm#7885 & doctrine/orm#8293 there was a new function introduced to loop.
Add a rule the will move all iterate()
to toIterable()
. And make sure we fix cases like:
-$results = $em->select('e')->from('entity')->getQuery()->iterate();
+$results = $em->select('e')->from('entity')->getQuery()->toIterable();
foreach($resuls as $result) {
- $entityIsHere = $result[0];
+ $entityIsHere = $result;
}
Also create a doctrine-orm-28.php
- public function postPersist(Notification $notification, \Doctrine\ORM\Event\LifecycleEventArgs $args) : void
+ public function postPersist(Notification $notification, \Doctrine\ORM\Event\PostPersistEventArgs $args) : void
- public function postUpdate(Notification $notification, \Doctrine\ORM\Event\LifecycleEventArgs $args) : void
+ public function postUpdate(Notification $notification, \Doctrine\ORM\Event\PostUpdateEventArgs $args) : void
` ``
The set-list DOCTRINE_CODE_QUALITY
changes this:
#[ORM\Column(type: 'decimal', precision: 9, scale: 2)]
protected $amount;
into this:
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DECIMAL, precision: 9, scale: 2)]
protected ?string $amount = null;
The field nullable
on ORM\Column
is false
by default. But the set-list seems to imply it to be true
, adding an undesired ?
and initializing the field with null
.
While working on one open source tool when Entity has both Annotation and Attributes for B/C.
Executed rector and it created dublicate Attributes.
It would be great to validate attributes before rule is applied and if Attributes alter exists, just remove the Annotation.
When running rector with the doctrine exension:
$containerConfigurator->import(DoctrineSetList::DOCTRINE_CODE_QUALITY);
1) src/Cashback/Domain/Model/CashbackClaim.php:57
---------- begin diff ----------
@@ @@
/**
* @ORM\Column(type="datetime", nullable=true)
*/
- private ?DateTimeInterface $orderDetailsOrderDate = null;
+ private ?DateTimeInterface $orderDetailsOrderDate;
/**
* @ORM\Column(length="256")
@@ @@
@@ @@
{
$uuid = Uuid::uuid4();
$this->entityId = $uuid->toString();
+ $this->orderDetailsOrderDate = new \DateTime(null);
}
The result is unexpected as it did add:
$this->orderDetailsOrderDate = new \DateTime(null);
Which should just stay as:
private ?DateTimeInterface $orderDetailsOrderDate = null;
e.g. this throws a NotYetImplementedException:
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
*/
class User
{
public const DEFAULT = false;
/**
* @ORM\Column(name="is_old", type="boolean")
*/
private $isOld = self::DEFAULT;
}
see https://getrector.org/demo/5ec50f90-39ba-4292-8da4-8573f90ef578
I guess that the check here should also check if $defaultExpr is an instance of ClassConstFetch. I'll send a PR for this.
One Question though: Is there a reason why refactorToBoolType() returns in case of Constants, but refactorToIntType() does not?
Before:
#[ORM\Column(type: Types::JSON, nullable: true)]
After:
#[ORM\Column(type: Types::JSON, nullable: true, options: ['jsonb' => true])]
The JSONB is the modern json type for postgresql and is mostly the type you want to use. It would nice to have a rector rule for it to force it in a project.
When applying the rule TypedPropertyFromColumnTypeRector
See https://getrector.org/demo/1ec73834-ba14-69bc-8cfe-29f5df412c73
Doctrine decimal type should be mapped to string to preserve precision as per:
https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#mapping-matrix
https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#decimal
In this case we can also use numeric-string
var annotation to specify that it's a numeric string and not just any other string.
The rule RemoveRedundantDefaultPropertyAnnotationValuesRector is adding one more line instead of replacing the existing one.
It only occurs when there are more annotations below the @Orm\Column. If i move the @expose and @JmsGroups lines to the line before @Orm\Column it works like expected.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups as JmsGroups;
/**
* @ORM\Entity()
*/
class Teste
{
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, nullable=false)
*
* @Expose
* @JmsGroups({"list", "details"})
*/
private $name;
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
$ vendor/bin/rector process src/Entity/Teste.php --dry-run --debug
src/Entity/Teste.php
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\ManagerRegistryGetManagerToEntityManagerRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\InitializeDefaultEntityCollectionRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\MoveCurrentDateTimeDefaultInEntityToConstructorRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\RemoveRedundantDefaultClassAnnotationValuesRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\MakeEntityDateTimePropertyDateTimeInterfaceRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\CorrectDefaultTypesOnEntityPropertyRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ChangeBigIntEntityPropertyToIntTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\RemoveRedundantDefaultPropertyAnnotationValuesRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromColumnTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromToOneRelationTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromToManyRelationTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromDoctrineCollectionRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\ClassMethod\MakeEntitySetterNullabilityInSyncWithPropertyRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\ClassMethod\MakeEntitySetterNullabilityInSyncWithPropertyRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\ManagerRegistryGetManagerToEntityManagerRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\InitializeDefaultEntityCollectionRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\MoveCurrentDateTimeDefaultInEntityToConstructorRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Class_\RemoveRedundantDefaultClassAnnotationValuesRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\MakeEntityDateTimePropertyDateTimeInterfaceRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\CorrectDefaultTypesOnEntityPropertyRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ChangeBigIntEntityPropertyToIntTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromToOneRelationTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromToManyRelationTypeRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\TypedPropertyFromDoctrineCollectionRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\ClassMethod\MakeEntitySetterNullabilityInSyncWithPropertyRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\ClassMethod\MakeEntitySetterNullabilityInSyncWithPropertyRector
[file] src/Entity/Teste.php
[rule] Rector\Doctrine\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector
1 file with changes
===================
1) src/Entity/Teste.php:13
---------- begin diff ----------
@@ @@
/**
* @var string
*
+ * @ORM\Column(name="name", type="string", length=255)
* @ORM\Column(name="name", type="string", length=255, nullable=false)
*
* @Expose
* @JmsGroups({"list", "details"})
*/
- private $name;
+ private ?string $name = null;
public function getName(): ?string
{
----------- end diff -----------
Applied rules:
* RemoveRedundantDefaultPropertyAnnotationValuesRector (https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/basic-mapping.html#property-mapping)
* TypedPropertyFromColumnTypeRector
There is an issue in doctrine migration which add some unexpected schema migrations in the down migration: doctrine/dbal#1110:
In my project it did contain the follwing:
$this->addSql('CREATE SCHEMA metric_helpers');
$this->addSql('CREATE SCHEMA user_management');
$this->addSql('CREATE SCHEMA public');
Think rector could be used to make sure this addSql never exists.
In the end, 3 attributes should be created
Lets add a rule to replace event strings with the corresponding class constants (Just like the Criteria
constants)
<?php
declare(strict_types=1);
namespace App\Subscriber\Doctrine;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
+use Doctrine\ORM\Events;
class DoctrineSubscriber implements EventSubscriber
{
public function getSubscribedEvents(): array
{
return [
- 'prePersist',
- 'preUpdate',
+ Events::prePersist,
+ Events::preUpdate,
];
}
public function prePersist(LifecycleEventArgs $args) {
//...
}
public function preUpdate(PreUpdateEventArgs $eventArgs) {
//...
}
}
I'll see if I can implement this in the future
Subject | Details |
---|---|
Rector version | last dev-main |
Installed as | composer dependency |
See https://getrector.com/demo/720d70fc-be2c-4a49-a67d-6c35f25f2d76
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
final class DemoEntity
{
/**
* @ORM\Column(type="decimal", scale=1, precision=4, nullable=false)
*/
protected $productDiscount = 0;
}
TypedPropertyFromColumnTypeRector
/**
* @ORM\Column(type="decimal", scale=1, precision=4, nullable=false)
*/
- protected $productDiscount = 0;
+ protected float $productDiscount = 0;
This rule is removing JoinColumn annotation
- * @ORM\JoinTable(name="vouchers_subscriptions",
- * joinColumns={@ORM\JoinColumn(name="voucher_id", referencedColumnName="id")},
- * inverseJoinColumns={@ORM\JoinColumn(name="subscription_id", referencedColumnName="id")}
- * )
+ * @ORM\JoinTable(name="vouchers_subscriptions", joinColumns={(name="voucher_id")}, inverseJoinColumns={(name="subscription_id", referencedColumnName="id")})
But in the doc it needs of JoinColumn
hello is there some rules for doctrine odm mongodb ?
Probably generic rule in rector/rector-src that convers attribute possition value to constant map:
https://github.com/doctrine/dbal/blob/3.1.x/src/Types/Types.php
I'm trying out the new ORM 2.9 upgrade set to convert Annotations to Attributes and I found a problem.
Given I have the following entity:
/**
* @ORM\Table(
* name="table",
* options={"charset": "utf8mb4", "collate": "utf8mb4_bin"},
* uniqueConstraints={
* @ORM\UniqueConstraint(name="unique_review", columns={"type", "relationship_id", "user_id"})
* },
* indexes={
* @ORM\Index(name="index_name", columns={"user_id", "type", "relationship_id", "created_at", "is_deleted"}),
* },
* )
*/
class SomeEntity
Rector changes this to:
#[Table(name: 'table', options: ['charset' => 'utf8mb4', 'collate' => 'utf8mb4_bin'], uniqueConstraints: ['(name="unique_review", columns={"type", "relationship_id", "user_id"})'], indexes: ['(name="index_name", columns={"user_id", "type", "relationship_id", "created_at", "is_deleted"})'], ')')]
class SomeEntity
As you can see, it completely trips on the nested annotations. And just dumps them in as strings.
Turns out that nested attributes are not supported.
The solution would be, to move the nested annotations to attributes on a new line, like this:
#[Table(name: 'table', options: ['charset' => 'utf8mb4', 'collate' => 'utf8mb4_bin'])]
#[UniqueConstraint(name: 'unique_review', columns=['type', 'relationship_id', 'user_id']]
#[Index(name: 'index_name', columns=['user_id', 'type', 'relationship_id', 'created_at', 'is_deleted'])]
class SomeEntity
To mitigate this bug, I tried to first manually convert the nested Annotations to Class Annotations. But Doctrine doesn't allow that:
/**
* @ORM\Table(name="table", options={"charset": "utf8mb4", "collate": "utf8mb4_bin"})
* @ORM\UniqueConstraint(name="unique_review", columns={"type", "relationship_id", "user_id"})
* @ORM\Index(name="index_name", columns={"user_id", "type", "relationship_id", "created_at", "is_deleted"})
*/
class SomeEntity
[Semantical Error] Annotation @ORM\UniqueConstraint is not allowed to be declared on class SomeEntity.
You may only use this annotation on these code elements: ANNOTATION.
That also means that these examples are impossible/invalid:
https://github.com/rectorphp/rector-doctrine/blob/main/tests/Set/DoctrineORM29Set/Fixture/unique_constraint.php.inc
https://github.com/rectorphp/rector-doctrine/blob/main/tests/Set/DoctrineORM29Set/Fixture/index.php.inc
With doctrine-dbal-211.php we added multiple rename method rules for:
Doctrine\\DBAL\\Connection
and
Doctrine\\DBAL\\Statement
i Have the following query that wont be updated:
return (int) $this->_em->getConnection()
->executeQuery("SELECT x")
->fetchColumn();
as executeQuery returns a Doctrine\DBAL\Result
, we should probably add this one as well as when i update the function call it works as expected the rule looks like:
new MethodCallRename('Doctrine\\DBAL\\Result', 'fetchColumn', 'fetchOne'),
Can i add this?
Some other rules which don't work with entities annotated using php attributes instead of doc blocks
Working on annotations:
https://getrector.org/demo/1ec7452c-d213-6766-899c-53572d5e860c
No change on attributes:
https://getrector.org/demo/1ec7452b-e5a8-6bc4-b0d4-752ae0c535c0
We recently converted our doctrine entities to use php 8 attributes using rector and now we wish to apply some other rules but it seems like many rules don't work on attribute based entities.
I tried TypedPropertyFromColumnTypeRector
rule, and it doesn't work on attribute based entities. It just shows Rector is done!
message without changing anything even though there are many properties left un-typed.
To test I tried the same rule on old annotation based entity and it worked well.
Is there any way to use these rules now or will the attribute based entities require new rules?
Replace doctrine's deprecated fetchAll() with the new methods.
Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
$result = $this->connection
->executeQuery($sql)
- ->fetchAll(\PDO::FETCH_COLUMN);
+ ->fetchFirstColumn();
$result = $qb->execute()
- ->fetchAll(\PDO::FETCH_ASSOC);
+ ->fetchAllAssociative();
$result = $this->connection
->executeQuery($sql)
- ->fetchColumn();
+ ->fetchOne();
getEntityManager from OnFlushEventArgs (and others) is depreacated since 2.13 see deprecated function:
/**
* Retrieve associated EntityManager.
*
* @deprecated 2.13. Use {@see getObjectManager} instead.
*
* @return EntityManagerInterface
*/
public function getEntityManager()
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/9875',
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObjectManager() instead.',
__METHOD__
);
return $this->getObjectManager();
}
I like to create a rule which will change function call from getEntityManager
to getObjectManager
and create a constant DOCTRINE_213
that will have this rule let me know if that is something we want
For example, if using custom types:
<?php
\Doctrine\DBAL\Types\Type::addType('money', 'My\Project\Types\MoneyType');
would be updated to:
<?php
\Doctrine\DBAL\Types\Types::addType('money', 'My\Project\Types\MoneyType');
Which is invalid since these methods only exist on the Doctrine\DBAL\Types\Type
version.
Introduced in rectorphp/rector#2514.
Looks like there's a bug on the ManyToOne relation with the JoinColumns annotation which ends out with the following error.
[Syntax Error] Expected PlainValue, got '(' at position 166 in property App\Entity\Product::$inventoryPosition.
The documentation says the @JoinColumns needs an array of JoinColumn:
An array of @joincolumn annotations for a @ManyToOne or @OneToOne relation with an entity that has multiple identifiers.
Take a look the code below removing the @Orm\JoinColumn annotation.
/**
* @var InventoryPosition
*
* @ORM\ManyToOne(targetEntity="App\Entity\InventoryPosition", inversedBy="products", cascade={"persist"})
- * @ORM\JoinColumns({
- * @ORM\JoinColumn(name="inventory_position_id", referencedColumnName="id")
- * })
+ * @ORM\JoinColumns({(name="inventory_position_id")})
*/
- private $inventoryPosition;
+ private ?\App\Entity\InventoryPosition $inventoryPosition = null;
I'm using the DoctrineSetList::DOCTRINE_CODE_QUALITY
setlist
I have an entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use SimpleThings\EntityAudit\Mapping\Annotation as Audit;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Table(name="products", indexes={
* @ORM\Index(columns={"updated_at"})
* }))
* @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
* @Serializer\ExclusionPolicy("ALL")
* @Audit\Auditable()
*/
class TestEntity
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id()
* @Serializer\SerializedName("id")
* @Serializer\Type("integer")
* @Assert\NotBlank()
*/
protected $id;
}
After running rector with only one set DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES
, it turns into:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use SimpleThings\EntityAudit\Mapping\Annotation as Audit;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Table(name: 'products')]
#[ORM\Index(columns: ['updated_at'])]
class TestEntity
{
/**
* @var int
*
* @Serializer\SerializedName("id")
* @Serializer\Type("integer")
* @Assert\NotBlank()
*/
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\Id]
protected $id;
}
So these annotations have gone:
* @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
* @Serializer\ExclusionPolicy("ALL")
* @Audit\Auditable()
Whereas on the id
property, the unprocessed annotations are in place.
Is that a bug or feature?
Rector 0.15.10
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.