mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-29 06:03:30 +00:00
[CakePHPToSymfony] Add CakePHPModelToDoctrineRepositoryRector
This commit is contained in:
parent
4d5b203242
commit
a63038b95c
8
.github/workflows/test_with_doctrine.yaml
vendored
8
.github/workflows/test_with_doctrine.yaml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: Test_With_Doctrine
|
||||
name: Test With Doctrine
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request: null
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
@ -15,6 +15,7 @@ jobs:
|
|||
with:
|
||||
php-version: 7.3
|
||||
coverage: none
|
||||
|
||||
- name: Clone doctrine/orm and install safe dependencies
|
||||
run: |
|
||||
# cannot install dev deps (--no-dev), because doctrine/orm might inherit from them in different version
|
||||
|
@ -30,5 +31,4 @@ jobs:
|
|||
# do not intall doctrine/orm phpstan, it conflicts with Retor's one
|
||||
composer install -d orm --no-dev
|
||||
|
||||
- run: |
|
||||
bin/rector process orm/lib --set dead-code --autoload-file orm/vendor/autoload.php
|
||||
- run: bin/rector process orm/lib --set dead-code --autoload-file orm/vendor/autoload.php
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# from CakePHP 2.4.6 to Symfony 5.1 (May 2020) and Twig 3
|
||||
# from CakePHP 2.4.6 (April 2014) to Symfony 5.1 (May 2020) and Twig 3
|
||||
services:
|
||||
# https://github.com/cakephp/cakephp/blob/2.4.6/lib/Cake/Controller/Controller.php
|
||||
Rector\CakePHPToSymfony\Rector\Class_\CakePHPControllerToSymfonyControllerRector: null
|
||||
|
@ -12,4 +12,7 @@ services:
|
|||
Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateLinkToTwigRector: null
|
||||
Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateTranslateToTwigRector: null
|
||||
Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateHToTwigRector: null
|
||||
|
||||
# Model to Doctrine
|
||||
Rector\CakePHPToSymfony\Rector\Class_\CakePHPModelToDoctrineEntityRector: null
|
||||
Rector\CakePHPToSymfony\Rector\Class_\CakePHPModelToDoctrineRepositoryRector: null
|
|
@ -1,4 +1,4 @@
|
|||
# All 443 Rectors Overview
|
||||
# All 444 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
|
@ -454,6 +454,63 @@ Migrate CakePHP Model active record to Doctrine\ORM Entity and EntityRepository
|
|||
|
||||
<br>
|
||||
|
||||
### `CakePHPModelToDoctrineRepositoryRector`
|
||||
|
||||
- class: `Rector\CakePHPToSymfony\Rector\Class_\CakePHPModelToDoctrineRepositoryRector`
|
||||
|
||||
Migrate CakePHP Model active record to Doctrine\ORM\Repository with repository/DQL method calls
|
||||
|
||||
```diff
|
||||
-class Activity extends \AppModel
|
||||
+use Doctrine\ORM\EntityManagerInterface;
|
||||
+
|
||||
+class Activity
|
||||
{
|
||||
+}
|
||||
+
|
||||
+class ActivityRepository
|
||||
+{
|
||||
+ /**
|
||||
+ * @var EntityManagerInterface
|
||||
+ */
|
||||
+ private $repository;
|
||||
+
|
||||
+ public function __construct(EntityManagerInterface $entityManager)
|
||||
+ {
|
||||
+ $this->repository = $entityManager->getRepository(Activity::class);
|
||||
+ }
|
||||
+
|
||||
public function getAll()
|
||||
{
|
||||
- $result = $this->find('all');
|
||||
+ $result = $this->repository->findAll();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getOne()
|
||||
{
|
||||
- $result = $this->find('first', [
|
||||
- 'conditions' => [
|
||||
- 'DocumentVersionsSave.revision_number' => $versionId,
|
||||
- 'DocumentVersionsSave.document_id' => $documentId,
|
||||
- ],
|
||||
- 'order' => [
|
||||
- 'created DESC',
|
||||
- ],
|
||||
- ]);
|
||||
+ $result = $this->findOneBy([
|
||||
+ 'revision_number' => $versionId,
|
||||
+ 'document_id' => $documentId,
|
||||
+ ], 'created DESC');
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### `CakePHPTemplateHToTwigRector`
|
||||
|
||||
- class: `Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateHToTwigRector`
|
||||
|
|
1
ecs.yaml
1
ecs.yaml
|
@ -132,6 +132,7 @@ parameters:
|
|||
Symplify\CodingStandard\Sniffs\ControlStructure\SprintfOverContactSniff:
|
||||
# respects inherited pattern for better comparing
|
||||
- 'src/PhpParser/Printer/BetterStandardPrinter.php'
|
||||
- 'src/Rector/AbstractRector/BetterStandardPrinterTrait.php'
|
||||
|
||||
PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\AssignmentInConditionSniff.FoundInWhileCondition: null
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ use PhpParser\Node;
|
|||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\CakePHPToSymfony\Rector\AbstractCakePHPRector;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
|
@ -27,20 +26,14 @@ final class CakePHPControllerComponentToSymfonyRector extends AbstractCakePHPRec
|
|||
*/
|
||||
private $classManipulator;
|
||||
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $componentsClasses = [];
|
||||
|
||||
public function __construct(ClassManipulator $classManipulator, ClassNaming $classNaming)
|
||||
public function __construct(ClassManipulator $classManipulator)
|
||||
{
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
|
@ -131,7 +124,7 @@ PHP
|
|||
$oldProperyNameToNewPropertyName = [];
|
||||
|
||||
foreach ($componentClasses as $componentName => $componentClass) {
|
||||
$componentClassShortName = $this->classNaming->getShortName($componentClass);
|
||||
$componentClassShortName = $this->getShortName($componentClass);
|
||||
$propertyShortName = lcfirst($componentClassShortName);
|
||||
$this->addPropertyToClass($node, new ObjectType($componentClass), $propertyShortName);
|
||||
|
||||
|
@ -179,7 +172,7 @@ PHP
|
|||
|
||||
foreach ($componentNames as $componentName) {
|
||||
foreach ($componentsClasses as $componentClass) {
|
||||
$shortComponentClass = $this->classNaming->getShortName($componentClass);
|
||||
$shortComponentClass = $this->getShortName($componentClass);
|
||||
if (! Strings::startsWith($shortComponentClass, $componentName)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Rector\Class_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\CakePHPToSymfony\Rector\AbstractCakePHPRector;
|
||||
use Rector\CakePHPToSymfony\Rector\NodeFactory\DoctrineNodeFactory;
|
||||
use Rector\CakePHPToSymfony\Rector\NodeFactory\DoctrineRepositoryClassMethodFactory;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
/**
|
||||
* @see https://book.cakephp.org/2/en/models/retrieving-your-data.html#find
|
||||
* @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/getting-started.html#entity-repositories
|
||||
*
|
||||
* @see \Rector\CakePHPToSymfony\Tests\Rector\Class_\CakePHPModelToDoctrineRepositoryRector\CakePHPModelToDoctrineRepositoryRectorTest
|
||||
*/
|
||||
final class CakePHPModelToDoctrineRepositoryRector extends AbstractCakePHPRector
|
||||
{
|
||||
/**
|
||||
* @var DoctrineRepositoryClassMethodFactory
|
||||
*/
|
||||
private $doctrineRepositoryClassMethodFactory;
|
||||
|
||||
/**
|
||||
* @var DoctrineNodeFactory
|
||||
*/
|
||||
private $doctrineNodeFactory;
|
||||
|
||||
public function __construct(
|
||||
DoctrineRepositoryClassMethodFactory $doctrineRepositoryClassMethodFactory,
|
||||
DoctrineNodeFactory $doctrineNodeFactory
|
||||
) {
|
||||
$this->doctrineRepositoryClassMethodFactory = $doctrineRepositoryClassMethodFactory;
|
||||
$this->doctrineNodeFactory = $doctrineNodeFactory;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Migrate CakePHP Model active record to Doctrine\ORM\Repository with repository/DQL method calls',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
class Activity extends \AppModel
|
||||
{
|
||||
public function getAll()
|
||||
{
|
||||
$result = $this->find('all');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getOne()
|
||||
{
|
||||
$result = $this->find('first', [
|
||||
'conditions' => [
|
||||
'DocumentVersionsSave.revision_number' => $versionId,
|
||||
'DocumentVersionsSave.document_id' => $documentId,
|
||||
],
|
||||
'order' => [
|
||||
'created DESC',
|
||||
],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class Activity
|
||||
{
|
||||
}
|
||||
|
||||
class ActivityRepository
|
||||
{
|
||||
/**
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->repository = $entityManager->getRepository(Activity::class);
|
||||
}
|
||||
|
||||
public function getAll()
|
||||
{
|
||||
$result = $this->repository->findAll();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getOne()
|
||||
{
|
||||
$result = $this->findOneBy([
|
||||
'revision_number' => $versionId,
|
||||
'document_id' => $documentId,
|
||||
], 'created DESC');
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
PHP
|
||||
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isInCakePHPController($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$repositoryMethods = $this->getRepositoryMethods($node);
|
||||
if ($repositoryMethods === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. create repository class
|
||||
$repositoryClass = $this->createRepositoryClass($node, $repositoryMethods);
|
||||
|
||||
// 2. save repository class
|
||||
$nodeToPrint = $this->createNodeToPrint($node, $repositoryClass);
|
||||
|
||||
$repositoryFilePath = $this->createRepositoryFilePath($node);
|
||||
$this->printToFile($nodeToPrint, $repositoryFilePath);
|
||||
|
||||
// 3.remove repository class methods
|
||||
foreach ($repositoryMethods as $repositoryMethod) {
|
||||
$this->removeNode($repositoryMethod);
|
||||
}
|
||||
|
||||
$node->extends = null;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for "$this->find()" call
|
||||
*/
|
||||
private function inRepositoryMethod(ClassMethod $classMethod): bool
|
||||
{
|
||||
$isRepositoryMethod = false;
|
||||
|
||||
$this->traverseNodesWithCallable((array) $classMethod->getStmts(), function (Node $node) use (
|
||||
&$isRepositoryMethod
|
||||
) {
|
||||
if (! $node instanceof MethodCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isObjectType($node->var, 'AppModel')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isName($node->name, 'find')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$isRepositoryMethod = true;
|
||||
|
||||
return NodeTraverser::STOP_TRAVERSAL;
|
||||
});
|
||||
|
||||
return $isRepositoryMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMethod[]
|
||||
*/
|
||||
private function getRepositoryMethods(Class_ $class): array
|
||||
{
|
||||
$repositoryMethods = [];
|
||||
|
||||
foreach ($class->getMethods() as $classMethod) {
|
||||
if (! $this->inRepositoryMethod($classMethod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$repositoryMethods[] = $classMethod;
|
||||
}
|
||||
|
||||
return $repositoryMethods;
|
||||
}
|
||||
|
||||
private function createNodeToPrint(Class_ $class, Class_ $repositoryClass): Node
|
||||
{
|
||||
/** @var Namespace_|null $namespaceNode */
|
||||
$namespaceNode = $class->getAttribute(AttributeKey::NAMESPACE_NODE);
|
||||
if ($namespaceNode !== null) {
|
||||
$namespaceNode->stmts = [$repositoryClass];
|
||||
return $namespaceNode;
|
||||
}
|
||||
|
||||
return $repositoryClass;
|
||||
}
|
||||
|
||||
private function createRepositoryFilePath(Class_ $class): string
|
||||
{
|
||||
$repositoryClassName = $this->getRepositoryShortClassName($class);
|
||||
|
||||
/** @var SmartFileInfo $fileInfo */
|
||||
$fileInfo = $class->getAttribute(AttributeKey::FILE_INFO);
|
||||
|
||||
return $fileInfo->getRelativeDirectoryPath() . '/' . $repositoryClassName . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod[] $repositoryMethods
|
||||
*/
|
||||
private function createRepositoryClass(Class_ $class, array $repositoryMethods): Class_
|
||||
{
|
||||
$repositoryClassName = $this->getRepositoryShortClassName($class);
|
||||
$repositoryClass = new Class_($repositoryClassName);
|
||||
|
||||
$repositoryClass->stmts[] = $this->doctrineNodeFactory->createRepositoryProperty();
|
||||
|
||||
$entityClass = $this->getName($class->name);
|
||||
assert(is_string($entityClass));
|
||||
|
||||
$repositoryClass->stmts[] = $this->doctrineNodeFactory->createConstructorWithGetRepositoryAssign($entityClass);
|
||||
|
||||
foreach ($repositoryMethods as $repositoryMethod) {
|
||||
$doctrineRepositoryClassMethod = $this->doctrineRepositoryClassMethodFactory->createFromCakePHPClassMethod(
|
||||
$repositoryMethod,
|
||||
$entityClass
|
||||
);
|
||||
$repositoryClass->stmts[] = $doctrineRepositoryClassMethod;
|
||||
}
|
||||
|
||||
return $repositoryClass;
|
||||
}
|
||||
|
||||
private function getRepositoryShortClassName(Class_ $class): string
|
||||
{
|
||||
return $this->getShortName($class->name) . 'Repository';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Rector\NodeFactory;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
|
||||
final class DoctrineNodeFactory
|
||||
{
|
||||
/**
|
||||
* @var BuilderFactory
|
||||
*/
|
||||
private $builderFactory;
|
||||
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
public function __construct(BuilderFactory $builderFactory, DocBlockManipulator $docBlockManipulator)
|
||||
{
|
||||
$this->builderFactory = $builderFactory;
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates:
|
||||
* $this->repository = $entityManager->getRepository(\EntityClass::class);
|
||||
*/
|
||||
public function createRepositoryAssign(string $entityClass): Assign
|
||||
{
|
||||
$repositoryPropertyFetch = new PropertyFetch(new Variable('this'), new Identifier('repository'));
|
||||
|
||||
$entityClassReference = new ClassConstFetch(new FullyQualified($entityClass), 'class');
|
||||
|
||||
$getRepositoryMethodCall = new MethodCall(new Variable('entityManager'), 'getRepository', [
|
||||
new Arg($entityClassReference),
|
||||
]);
|
||||
|
||||
return new Assign($repositoryPropertyFetch, $getRepositoryMethodCall);
|
||||
}
|
||||
|
||||
public function createRepositoryProperty(): Property
|
||||
{
|
||||
$repositoryProperty = $this->builderFactory->property('repository')
|
||||
->makePrivate()
|
||||
->getNode();
|
||||
|
||||
$this->docBlockManipulator->changeVarTag(
|
||||
$repositoryProperty,
|
||||
new FullyQualifiedObjectType('Doctrine\ORM\EntityRepository')
|
||||
);
|
||||
|
||||
return $repositoryProperty;
|
||||
}
|
||||
|
||||
public function createConstructorWithGetRepositoryAssign(string $entityClass): ClassMethod
|
||||
{
|
||||
$param = $this->builderFactory->param('entityManager')
|
||||
->setType(new FullyQualified(EntityManagerInterface::class))
|
||||
->getNode();
|
||||
|
||||
$assign = $this->createRepositoryAssign($entityClass);
|
||||
|
||||
return $this->builderFactory->method('__construct')
|
||||
->makePublic()
|
||||
->addParam($param)
|
||||
->addStmt($assign)
|
||||
->getNode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Rector\NodeFactory;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PhpParser\Node\Value\ValueResolver;
|
||||
use Rector\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
|
||||
final class DoctrineRepositoryClassMethodFactory
|
||||
{
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
|
||||
public function __construct(
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
NameResolver $nameResolver,
|
||||
ValueResolver $valueResolver
|
||||
) {
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->valueResolver = $valueResolver;
|
||||
}
|
||||
|
||||
public function createFromCakePHPClassMethod(ClassMethod $classMethod, string $entityClass): ClassMethod
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable(
|
||||
(array) $classMethod->getStmts(),
|
||||
function (Node $node) use ($entityClass) {
|
||||
if (! $node instanceof MethodCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->nameResolver->isName($node->name, 'find')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->createMethodByKind($node, $entityClass);
|
||||
}
|
||||
);
|
||||
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
private function createMethodByKind(MethodCall $methodCall, string $entityClass): void
|
||||
{
|
||||
$findKind = $this->valueResolver->getValue($methodCall->args[0]->value);
|
||||
if ($findKind === 'all') {
|
||||
$methodCall->var = new PropertyFetch(new Variable('this'), 'repository');
|
||||
$methodCall->name = new Identifier('findAll');
|
||||
$methodCall->args = [];
|
||||
} elseif ($findKind === 'first') {
|
||||
$methodCall->name = new Identifier('findOneBy');
|
||||
unset($methodCall->args[0]);
|
||||
|
||||
if (! isset($methodCall->args[1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$firstArgument = $methodCall->args[1]->value;
|
||||
assert($firstArgument instanceof Array_);
|
||||
|
||||
$methodCall->args = $this->createFindOneByArgs($entityClass, $firstArgument);
|
||||
} else {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private function getItemByKey(Array_ $array, string $key): ?ArrayItem
|
||||
{
|
||||
foreach ($array->items as $arrayItem) {
|
||||
if ($arrayItem->key === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->valueResolver->getValue($arrayItem->key) !== $key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $arrayItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function clearStringFromEntityPrefix(String_ $string, string $entityClass): String_
|
||||
{
|
||||
$string->value = Strings::replace($string->value, '#^' . $entityClass . '\.#');
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Arg[]
|
||||
*/
|
||||
private function createFindOneByArgs(string $entityClass, Array_ $firstArgument): array
|
||||
{
|
||||
$args = [];
|
||||
|
||||
/** @var Array_ $firstArgument */
|
||||
$conditionsArrayItem = $this->getItemByKey($firstArgument, 'conditions');
|
||||
if ($conditionsArrayItem !== null && $conditionsArrayItem->value instanceof Array_) {
|
||||
$conditionArray = $conditionsArrayItem->value;
|
||||
$this->removeEntityPrefixFromConditionKeys($conditionArray, $entityClass);
|
||||
$args[] = new Arg($conditionArray);
|
||||
}
|
||||
|
||||
$orderItem = $this->getItemByKey($firstArgument, 'order');
|
||||
if ($orderItem !== null) {
|
||||
if (count($args) === 0) {
|
||||
$args[] = new Arg(new ConstFetch(new Name('null')));
|
||||
}
|
||||
|
||||
/** @var Array_ $orderArray */
|
||||
$orderArray = $orderItem->value;
|
||||
assert(isset($orderArray->items[0]));
|
||||
$args[] = new Arg($orderArray->items[0]->value);
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function removeEntityPrefixFromConditionKeys(Array_ $conditionArray, string $entityClass): void
|
||||
{
|
||||
// clear keys from current class
|
||||
foreach ($conditionArray->items as $conditionArrayItem) {
|
||||
if (! $conditionArrayItem->key instanceof String_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$conditionArrayItem->key = $this->clearStringFromEntityPrefix($conditionArrayItem->key, $entityClass);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Tests\Rector\Class_\CakePHPModelToDoctrineRepositoryRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\CakePHPToSymfony\Rector\Class_\CakePHPModelToDoctrineRepositoryRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class CakePHPModelToDoctrineRepositoryRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideDataForTest()
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->doTestFile($file);
|
||||
|
||||
$repositoryFilePath = $this->getTempPath() . '/ActivityRepository.php';
|
||||
$this->assertFileExists($repositoryFilePath);
|
||||
$this->assertFileEquals(__DIR__ . '/Source/ExpectedActivityRepository.php', $repositoryFilePath);
|
||||
}
|
||||
|
||||
public function provideDataForTest(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return CakePHPModelToDoctrineRepositoryRector::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Tests\Rector\Class_\CakePHPModelToDoctrineRepositoryRector\Fixture;
|
||||
|
||||
class Activity extends \AppModel
|
||||
{
|
||||
public function getAll()
|
||||
{
|
||||
$result = $this->find('all');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getOne()
|
||||
{
|
||||
$result = $this->find('first', [
|
||||
'conditions' => [
|
||||
'Activity.revision_number' => $versionId,
|
||||
'Activity.document_id' => $documentId,
|
||||
],
|
||||
'order' => [
|
||||
'created DESC',
|
||||
],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Tests\Rector\Class_\CakePHPModelToDoctrineRepositoryRector\Fixture;
|
||||
|
||||
class Activity
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Tests\Rector\Class_\CakePHPModelToDoctrineRepositoryRector\Fixture;
|
||||
|
||||
class ActivityRepository
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\EntityRepository
|
||||
*/
|
||||
private $repository;
|
||||
public function __construct(\Doctrine\ORM\EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->repository = $entityManager->getRepository(\Activity::class);
|
||||
}
|
||||
public function getAll()
|
||||
{
|
||||
$result = $this->repository->findAll();
|
||||
return $result;
|
||||
}
|
||||
public function getOne()
|
||||
{
|
||||
$result = $this->findOneBy(['revision_number' => $versionId, 'document_id' => $documentId], 'created DESC');
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ namespace Rector\CodingStyle\Rector\Catch_;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Catch_;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
@ -17,16 +16,6 @@ use Rector\RectorDefinition\RectorDefinition;
|
|||
*/
|
||||
final class CatchExceptionNameMatchingTypeRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
public function __construct(ClassNaming $classNaming)
|
||||
{
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Type and name of catch exception should match', [
|
||||
|
@ -80,7 +69,7 @@ PHP
|
|||
}
|
||||
|
||||
$type = $node->types[0];
|
||||
$typeShortName = $this->classNaming->getShortName($type);
|
||||
$typeShortName = $this->getShortName($type);
|
||||
|
||||
$oldVariableName = $this->getName($node->var);
|
||||
if (! $oldVariableName) {
|
||||
|
|
|
@ -22,7 +22,6 @@ use PHPStan\Type\Type;
|
|||
use PHPStan\Type\UnionType;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\CodingStyle\Imports\ShortNameResolver;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PHPStan\Type\AliasedObjectType;
|
||||
|
@ -45,19 +44,13 @@ final class RemoveUnusedAliasRector extends AbstractRector
|
|||
*/
|
||||
private $resolvedDocPossibleAliases = [];
|
||||
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
/**
|
||||
* @var ShortNameResolver
|
||||
*/
|
||||
private $shortNameResolver;
|
||||
|
||||
public function __construct(ClassNaming $classNaming, ShortNameResolver $shortNameResolver)
|
||||
public function __construct(ShortNameResolver $shortNameResolver)
|
||||
{
|
||||
$this->classNaming = $classNaming;
|
||||
$this->shortNameResolver = $shortNameResolver;
|
||||
}
|
||||
|
||||
|
@ -163,7 +156,7 @@ PHP
|
|||
|
||||
$shortNames = $this->shortNameResolver->resolveForNode($use);
|
||||
foreach ($shortNames as $alias => $useImport) {
|
||||
$shortName = $this->classNaming->getShortName($useImport);
|
||||
$shortName = $this->getShortName($useImport);
|
||||
if ($shortName === $alias) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ use PhpParser\Node\Stmt\Return_;
|
|||
use PHPStan\Analyser\MutatingScope;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ParameterReflection;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Php70\ValueObject\VariableAssignPair;
|
||||
use Rector\PHPStan\Reflection\CallReflectionResolver;
|
||||
|
@ -48,15 +47,9 @@ final class NonVariableToVariableOnFunctionCallRector extends AbstractRector
|
|||
*/
|
||||
private $callReflectionResolver;
|
||||
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
public function __construct(CallReflectionResolver $callReflectionResolver, ClassNaming $classNaming)
|
||||
public function __construct(CallReflectionResolver $callReflectionResolver)
|
||||
{
|
||||
$this->callReflectionResolver = $callReflectionResolver;
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
|
@ -176,7 +169,7 @@ final class NonVariableToVariableOnFunctionCallRector extends AbstractRector
|
|||
private function getVariableNameFor(Expr $expr, Scope $scope): string
|
||||
{
|
||||
if ($expr instanceof New_ && $expr->class instanceof Name) {
|
||||
$name = $this->classNaming->getShortName($expr->class);
|
||||
$name = $this->getShortName($expr->class);
|
||||
} else {
|
||||
$name = $this->getName($expr);
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ PHP
|
|||
$this->alreadyProcessedClasses[] = $name;
|
||||
|
||||
$newName = $this->oldToNewClasses[$name];
|
||||
$newClassNamePart = $this->classNaming->getShortName($newName);
|
||||
$newClassNamePart = $this->getShortName($newName);
|
||||
$newNamespacePart = $this->classNaming->getNamespace($newName);
|
||||
|
||||
$this->ensureClassWillNotBeDuplicate($newName, $name);
|
||||
|
|
|
@ -228,3 +228,5 @@ parameters:
|
|||
|
||||
# known value
|
||||
- '#Access to undefined constant Rector\\BetterPhpDocParser\\PhpDocNode\\AbstractTagValueNode\:\:SHORT_NAME#'
|
||||
- '#Parameter \#1 \$name of method Rector\\Rector\\AbstractRector\:\:getShortName\(\) expects PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|string, PhpParser\\Node\\Identifier\|null given#'
|
||||
- '#Parameter \#1 \$entityClass of method Rector\\CakePHPToSymfony\\Rector\\NodeFactory\\DoctrineNodeFactory\:\:createConstructorWithGetRepositoryAssign\(\) expects string, string\|null given#'
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Rector\AbstractRector;
|
||||
|
||||
use Nette\Utils\FileSystem;
|
||||
use PhpParser\Node;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Printer\BetterStandardPrinter;
|
||||
|
@ -43,6 +44,16 @@ trait BetterStandardPrinterTrait
|
|||
return $this->betterStandardPrinter->print($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node|Node[]|null $node
|
||||
*/
|
||||
public function printToFile($node, string $filePath): void
|
||||
{
|
||||
$content = $this->print($node);
|
||||
$content = '<?php' . PHP_EOL . PHP_EOL . $content . PHP_EOL;
|
||||
FileSystem::write($filePath, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node|Node[]|null $firstNode
|
||||
* @param Node|Node[]|null $secondNode
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\Rector\AbstractRector;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
|
||||
/**
|
||||
|
@ -18,12 +19,18 @@ trait NameResolverTrait
|
|||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireNameResolverTrait(NameResolver $nameResolver): void
|
||||
public function autowireNameResolverTrait(NameResolver $nameResolver, ClassNaming $classNaming): void
|
||||
{
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function isName(Node $node, string $name): bool
|
||||
|
@ -48,4 +55,12 @@ trait NameResolverTrait
|
|||
{
|
||||
return $this->nameResolver->getName($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|Node\Name|Node\Identifier $name
|
||||
*/
|
||||
protected function getShortName($name): string
|
||||
{
|
||||
return $this->classNaming->getShortName($name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ use PhpParser\Node\Stmt\Class_;
|
|||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Declare_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\FileSystemRector\Rector\AbstractFileSystemRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
@ -21,16 +20,6 @@ use Symplify\SmartFileSystem\SmartFileInfo;
|
|||
*/
|
||||
final class MultipleClassFileToPsr4ClassesRector extends AbstractFileSystemRector
|
||||
{
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
public function __construct(ClassNaming $classNaming)
|
||||
{
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
|
@ -110,7 +99,7 @@ PHP
|
|||
continue;
|
||||
}
|
||||
|
||||
$classShortName = $this->classNaming->getShortName($className);
|
||||
$classShortName = $this->getShortName($className);
|
||||
if ($smartFileInfo->getBasenameWithoutSuffix() === $classShortName) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user