Skip to content

Commit

Permalink
migrate Doctrine entity type resolving for "find*" methods #1434
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed May 21, 2020
1 parent 43c5c95 commit ce87487
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}
}
2 changes: 1 addition & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.dic.SymfonyContainerTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.util.EventDispatcherTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryResultTypeProvider"/>
<typeProvider4 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryResultTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectManagerFindTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.assistant.signature.MethodSignatureTypeProvider"/>
<libraryRoot id="symfony_meta" path="/symfony-meta/" runtime="false"/>
Expand Down

0 comments on commit ce87487

Please sign in to comment.