-
-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
migrate Doctrine entity type resolving for "find*" methods #1434
- Loading branch information
Showing
2 changed files
with
51 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,32 +2,35 @@ | |
|
||
import com.intellij.openapi.project.Project; | ||
import com.intellij.openapi.util.text.StringUtil; | ||
import com.intellij.patterns.PlatformPatterns; | ||
import com.intellij.psi.PsiElement; | ||
import com.jetbrains.php.PhpIndex; | ||
import com.jetbrains.php.lang.parser.PhpElementTypes; | ||
import com.jetbrains.php.lang.psi.elements.Method; | ||
import com.jetbrains.php.lang.psi.elements.MethodReference; | ||
import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
import com.jetbrains.php.lang.psi.elements.PhpNamedElement; | ||
import com.jetbrains.php.lang.psi.resolve.types.PhpType; | ||
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3; | ||
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4; | ||
import fr.adrienbrault.idea.symfony2plugin.Settings; | ||
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher; | ||
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; | ||
import fr.adrienbrault.idea.symfony2plugin.util.PhpTypeProviderUtil; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Resolve "find*" at attach the entity | ||
* | ||
* "$om->getRepository('\Foo\Bar')->find('foobar')->get<caret>Id()" | ||
* | ||
* @author Daniel Espendiller <[email protected]> | ||
*/ | ||
public class ObjectRepositoryResultTypeProvider implements PhpTypeProvider3 { | ||
private static MethodMatcher.CallToSignature[] FIND_SIGNATURES = new MethodMatcher.CallToSignature[] { | ||
public class ObjectRepositoryResultTypeProvider implements PhpTypeProvider4 { | ||
private static final MethodMatcher.CallToSignature[] FIND_SIGNATURES = new MethodMatcher.CallToSignature[] { | ||
new MethodMatcher.CallToSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "find"), | ||
new MethodMatcher.CallToSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findOneBy"), | ||
new MethodMatcher.CallToSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findAll"), | ||
|
@@ -48,18 +51,7 @@ public char getKey() { | |
@Nullable | ||
@Override | ||
public PhpType getType(PsiElement e) { | ||
if (!Settings.getInstance(e.getProject()).pluginEnabled) { | ||
return null; | ||
} | ||
|
||
// filter out method calls without parameter | ||
// $this->get('service_name') | ||
if(!PlatformPatterns | ||
.psiElement(PhpElementTypes.METHOD_REFERENCE) | ||
.withChild(PlatformPatterns | ||
.psiElement(PhpElementTypes.PARAMETER_LIST) | ||
).accepts(e)) { | ||
|
||
if (!(e instanceof MethodReference) || !Settings.getInstance(e.getProject()).pluginEnabled) { | ||
return null; | ||
} | ||
|
||
|
@@ -105,57 +97,64 @@ public PhpType getType(PsiElement e) { | |
return new PhpType().add("#" + this.getKey() + refSignature + TRIM_KEY + repositorySignature); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) { | ||
// get back our original call | ||
int endIndex = expression.lastIndexOf(TRIM_KEY); | ||
public PhpType complete(String s, Project project) { | ||
int endIndex = s.lastIndexOf(TRIM_KEY); | ||
if(endIndex == -1) { | ||
return Collections.emptySet(); | ||
} | ||
|
||
String originalSignature = expression.substring(0, endIndex); | ||
String parameter = expression.substring(endIndex + 1); | ||
|
||
// search for called method | ||
PhpIndex phpIndex = PhpIndex.getInstance(project); | ||
Collection<? extends PhpNamedElement> phpNamedElementCollections = PhpTypeProviderUtil.getTypeSignature(phpIndex, originalSignature); | ||
if(phpNamedElementCollections.size() == 0) { | ||
return Collections.emptySet(); | ||
} | ||
|
||
Method method = getObjectRepositoryCall(phpNamedElementCollections); | ||
if(method == null) { | ||
return Collections.emptySet(); | ||
return null; | ||
} | ||
|
||
// we can also pipe php references signatures and resolve them here | ||
// overwrite parameter to get string value | ||
parameter = PhpTypeProviderUtil.getResolvedParameter(phpIndex, parameter); | ||
String originalSignature = s.substring(0, endIndex); | ||
String parameter = s.substring(endIndex + 1); | ||
parameter = PhpTypeProviderUtil.getResolvedParameter(PhpIndex.getInstance(project), parameter); | ||
if(parameter == null) { | ||
return Collections.emptySet(); | ||
return null; | ||
} | ||
|
||
PhpClass phpClass = EntityHelper.resolveShortcutName(project, parameter); | ||
if(phpClass == null) { | ||
return Collections.emptySet(); | ||
return null; | ||
} | ||
|
||
String name = method.getName(); | ||
if(name.equals("findAll") || name.equals("findBy")) { | ||
method.getType().add(phpClass.getFQN() + "[]"); | ||
return phpNamedElementCollections; | ||
PhpIndex phpIndex = PhpIndex.getInstance(project); | ||
|
||
Collection<? extends PhpNamedElement> typeSignature = PhpTypeProviderUtil.getTypeSignature(phpIndex, originalSignature); | ||
|
||
// ->getRepository(SecondaryMarket::class)->findAll() => "findAll", but only if its a instance of this method; | ||
// so non Doctrine method are already filtered | ||
Set<String> resolveMethods = getObjectRepositoryCall(typeSignature).stream() | ||
.map(PhpNamedElement::getName) | ||
.collect(Collectors.toSet()); | ||
|
||
if (resolveMethods.isEmpty()) { | ||
return null; | ||
} | ||
|
||
return PhpTypeProviderUtil.mergeSignatureResults(phpNamedElementCollections, phpClass); | ||
PhpType phpType = new PhpType(); | ||
|
||
resolveMethods.stream() | ||
.map(name -> name.equals("findAll") || name.equals("findBy") ? phpClass.getFQN() + "[]" : phpClass.getFQN()) | ||
.collect(Collectors.toSet()) | ||
.forEach(phpType::add); | ||
|
||
return phpType; | ||
} | ||
|
||
private Method getObjectRepositoryCall(Collection<? extends PhpNamedElement> phpNamedElements) { | ||
@Override | ||
public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) { | ||
return null; | ||
} | ||
|
||
@NotNull | ||
private Collection<Method> getObjectRepositoryCall(Collection<? extends PhpNamedElement> phpNamedElements) { | ||
Collection<Method> methods = new HashSet<>(); | ||
for (PhpNamedElement phpNamedElement: phpNamedElements) { | ||
if(phpNamedElement instanceof Method && PhpElementsUtil.isMethodInstanceOf((Method) phpNamedElement, FIND_SIGNATURES)) { | ||
return (Method) phpNamedElement; | ||
methods.add((Method) phpNamedElement); | ||
} | ||
} | ||
|
||
return null; | ||
return methods; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters