diff --git a/composer.json b/composer.json index 9cb6d63e..65e26936 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ }, "require-dev": { "doctrine/doctrine-bundle": "^3.0", - "doctrine/orm": "^3.2", + "doctrine/orm": "^3.7", "symplify/easy-coding-standard": "^13.2", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.2", diff --git a/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/DoctrineOrderByAttributeSortDirectionRectorTest.php b/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/DoctrineOrderByAttributeSortDirectionRectorTest.php new file mode 100644 index 00000000..68545e46 --- /dev/null +++ b/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/DoctrineOrderByAttributeSortDirectionRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/configured_rule.php'; + } +} diff --git a/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/Fixture/replace_orm_order_by.php.inc b/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/Fixture/replace_orm_order_by.php.inc new file mode 100644 index 00000000..37336d79 --- /dev/null +++ b/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/Fixture/replace_orm_order_by.php.inc @@ -0,0 +1,35 @@ + 'ASC'])] + protected \DateTimeInterface $messages; + + #[ORM\OrderBy(['updatedAt' => 'desc', 'id' => Order::Ascending])] + protected \DateTimeInterface $logs; +} + +?> +----- + \SortDirection::Ascending])] + protected \DateTimeInterface $messages; + + #[ORM\OrderBy(['updatedAt' => \SortDirection::Descending, 'id' => \SortDirection::Ascending])] + protected \DateTimeInterface $logs; +} + +?> diff --git a/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/configured_rule.php b/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/configured_rule.php new file mode 100644 index 00000000..87975cf6 --- /dev/null +++ b/rules-tests/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector/configured_rule.php @@ -0,0 +1,9 @@ +withRules([DoctrineOrderByAttributeSortDirectionRector::class]); diff --git a/rules/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector.php b/rules/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector.php new file mode 100644 index 00000000..d43034c2 --- /dev/null +++ b/rules/Orm37/Rector/Attribute/DoctrineOrderByAttributeSortDirectionRector.php @@ -0,0 +1,132 @@ + 'ASC'])] + protected $messages; +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use Doctrine\ORM\Mapping as ORM; + +class SomeClass +{ + #[ORM\OrderBy(['createdAt' => \SortDirection::Ascending])] + protected $messages; +} +CODE_SAMPLE + ), + ] + ); + } + + public function provideComposerPackageConstraint(): ComposerPackageConstraint + { + return new ComposerPackageConstraint('doctrine/orm', '>=3.7'); + } + + public function getNodeTypes(): array + { + return [Attribute::class]; + } + + /** + * @param Attribute $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isName($node->name, 'Doctrine\ORM\Mapping\OrderBy')) { + return null; + } + + $args = $node->args; + if ($args === []) { + return null; + } + + $firstArgValue = $args[0]->value; + + if (! $firstArgValue instanceof Array_) { + return null; + } + + $hasChanged = false; + + foreach ($firstArgValue->items as $arrayItem) { + $direction = $this->resolveSortDirectionValue($arrayItem->value); + + if ($direction === 'asc') { + $arrayItem->value = $this->nodeFactory->createClassConstFetch('SortDirection', 'Ascending'); + $hasChanged = true; + } elseif ($direction === 'desc') { + $arrayItem->value = $this->nodeFactory->createClassConstFetch('SortDirection', 'Descending'); + $hasChanged = true; + } + } + + return $hasChanged ? $node : null; + } + + private function resolveSortDirectionValue(Node\Expr $expr): ?string + { + if ($expr instanceof ClassConstFetch) { + $constName = $this->getName($expr->name); + + if (is_string($constName)) { + $normalized = strtolower($constName); + if (in_array($normalized, ['asc', 'ascending'], true)) { + return 'asc'; + } + if (in_array($normalized, ['desc', 'descending'], true)) { + return 'desc'; + } + } + } + + $value = $this->valueResolver->getValue($expr); + if (is_string($value)) { + $normalized = strtolower($value); + if ($normalized === 'asc' || $normalized === 'desc') { + return $normalized; + } + } + + return null; + } +}