Skip to content

Commit

Permalink
refactoring of ObjectManager::findBy* and support more possible repos…
Browse files Browse the repository at this point in the history
…itory usages #925 #898
  • Loading branch information
Haehnchen committed May 7, 2017
1 parent 97de010 commit 9791a8b
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 128 deletions.
1 change: 1 addition & 0 deletions META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dbal.DoctrineDbalQbGotoCompletionRegistrar"/>
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.type.DoctrineTypeGotoCompletionRegistrar"/>
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.DoctrineYamlGotoCompletionRegistrar"/>
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.ObjectRepositoryFindGotoCompletionRegistrar"/>
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.completion.xml.XmlGotoCompletionRegistrar"/>
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.dic.registrar.DicGotoCompletionRegistrar"/>
<GotoCompletionRegistrar implementation="fr.adrienbrault.idea.symfony2plugin.completion.yaml.YamlGotoCompletionRegistrar"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.PhpLanguage;
import com.jetbrains.php.lang.parser.PhpElementTypes;
import com.jetbrains.php.lang.psi.elements.*;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.dic.ConstraintPropertyReference;
import fr.adrienbrault.idea.symfony2plugin.dic.ServiceReference;
import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityHelper;
import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityReference;
import fr.adrienbrault.idea.symfony2plugin.doctrine.ModelFieldReference;
import fr.adrienbrault.idea.symfony2plugin.doctrine.dict.DoctrineTypes;
import fr.adrienbrault.idea.symfony2plugin.templating.TemplateReference;
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
Expand All @@ -20,8 +18,6 @@
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;

/**
* @author Daniel Espendiller <[email protected]>
*/
Expand Down Expand Up @@ -151,38 +147,6 @@ public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @No
}
);

psiReferenceRegistrar.registerReferenceProvider(
// @TODO: implement global pattern for array parameters
PlatformPatterns.psiElement(StringLiteralExpression.class).withParent(
PlatformPatterns.or(
PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE),
PlatformPatterns.psiElement(PhpElementTypes.ARRAY_KEY)
)
).inside(PlatformPatterns.psiElement(ParameterList.class)),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {

MethodMatcher.MethodMatchParameter methodMatchParameter = new MethodMatcher.ArrayParameterMatcher(psiElement, 0)
.withSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findOneBy")
.withSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findBy")
.match();

if(methodMatchParameter == null) {
return new PsiReference[0];
}

Collection<PhpClass> phpClasses = PhpElementsUtil.getClassFromPhpTypeSetArrayClean(psiElement.getProject(), methodMatchParameter.getMethodReference().getType().getTypes());
if(phpClasses.size() == 0) {
return new PsiReference[0];
}

return new PsiReference[]{ new ModelFieldReference((StringLiteralExpression) psiElement, phpClasses)};
}
}
);

psiReferenceRegistrar.registerReferenceProvider(
PlatformPatterns.psiElement(StringLiteralExpression.class),
new PsiReferenceProvider() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package fr.adrienbrault.idea.symfony2plugin.doctrine.metadata;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.PhpExpression;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil;
import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityHelper;
import fr.adrienbrault.idea.symfony2plugin.doctrine.dict.DoctrineModelField;
import fr.adrienbrault.idea.symfony2plugin.doctrine.dict.DoctrineModelFieldLookupElement;
import fr.adrienbrault.idea.symfony2plugin.doctrine.dict.DoctrineModelInterface;
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.util.DoctrineMetadataUtil;
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @author Daniel Espendiller <[email protected]>
*/
public class ObjectRepositoryFindGotoCompletionRegistrar implements GotoCompletionRegistrar {
public void register(GotoCompletionRegistrarParameter registrar) {

// "@var $om \Doctrine\Common\Persistence\ObjectManager"
// "$om->getRepository('Foo\Bar')->" + s + "(['foo' => 'foo', '<caret>' => 'foo'])"
registrar.register(PhpElementsUtil.getParameterListArrayValuePattern(), psiElement -> {
PsiElement context = psiElement.getContext();
if (!(context instanceof StringLiteralExpression)) {
return null;
}

MethodMatcher.MethodMatchParameter methodMatchParameter = new MethodMatcher.ArrayParameterMatcher(context, 0)
.withSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findOneBy")
.withSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findBy")
.match();

if(methodMatchParameter != null) {
MethodReference methodReference = methodMatchParameter.getMethodReference();

// extract from type provide on completion:
// $foo->getRepository('MODEL')->findBy()
Collection<PhpClass> phpClasses = PhpElementsUtil.getClassFromPhpTypeSetArrayClean(psiElement.getProject(), methodReference.getType().getTypes());

// resolve every direct repository instance $this->findBy()
// or direct repository instance $repository->findBy()
if(phpClasses.size() == 0) {
PhpExpression classReference = methodReference.getClassReference();
if(classReference != null) {
PhpType type = classReference.getType();
for (String s : type.getTypes()) {
// dont visit type providers
if(PhpType.isUnresolved(s)) {
continue;
}

for (DoctrineModelInterface doctrineModel : DoctrineMetadataUtil.findMetadataModelForRepositoryClass(psiElement.getProject(), s)) {
phpClasses.addAll(PhpElementsUtil.getClassesInterface(psiElement.getProject(), doctrineModel.getClassName()));
}
}
}
}

if(phpClasses.size() == 0) {
return null;
}

return new MyArrayFieldMetadataGotoCompletionRegistrar(psiElement, phpClasses);
}

return null;
});
}

private static class MyArrayFieldMetadataGotoCompletionRegistrar extends GotoCompletionProvider {
@NotNull
private final Collection<PhpClass> phpClasses;

MyArrayFieldMetadataGotoCompletionRegistrar(@NotNull PsiElement element, @NotNull Collection<PhpClass> phpClasses) {
super(element);
this.phpClasses = phpClasses;
}

@NotNull
@Override
public Collection<LookupElement> getLookupElements() {
List<LookupElement> results = new ArrayList<>();

phpClasses.forEach(phpClass ->
results.addAll(EntityHelper.getModelFields(phpClass).stream()
.map((Function<DoctrineModelField, LookupElement>) DoctrineModelFieldLookupElement::new)
.collect(Collectors.toList())
)
);

return results;
}

@NotNull
@Override
public Collection<PsiElement> getPsiTargets(PsiElement element) {
String content = GotoCompletionUtil.getTextValueForElement(element);
if(content == null) {
return Collections.emptyList();
}

Collection<PsiElement> results = new ArrayList<>();

phpClasses.forEach(phpClass ->
results.addAll(Arrays.asList(EntityHelper.getModelFieldTargets(phpClass, content)))
);

return results;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public static Collection<VirtualFile> findMetadataForRepositoryClass(final @NotN
project.putUserData(DOCTRINE_REPOSITORY_CACHE, cache);
}

repositoryClass = StringUtils.stripStart(repositoryClass,"\\");
if(!cache.getValue().containsKey(repositoryClass)) {
return Collections.emptyList();
}
Expand All @@ -151,6 +152,31 @@ public static Collection<VirtualFile> findMetadataForRepositoryClass(final @NotN
return virtualFiles;
}

/**
* Find metadata model in which the given repository class is used
* eg "@ORM\Entity(repositoryClass="FOOBAR")", xml or yaml
*/
@NotNull
public static Collection<DoctrineModelInterface> findMetadataModelForRepositoryClass(final @NotNull Project project, @NotNull String repositoryClass) {
repositoryClass = StringUtils.stripStart(repositoryClass,"\\");

Collection<DoctrineModelInterface> models = new ArrayList<>();

for (String key : FileIndexCaches.getIndexKeysCache(project, CLASS_KEYS, DoctrineMetadataFileStubIndex.KEY)) {
for (DoctrineModelInterface repositoryDefinition : FileBasedIndex.getInstance().getValues(DoctrineMetadataFileStubIndex.KEY, key, GlobalSearchScope.allScope(project))) {
String myRepositoryClass = repositoryDefinition.getRepositoryClass();
if(StringUtils.isBlank(myRepositoryClass) ||
!repositoryClass.equalsIgnoreCase(StringUtils.stripStart(myRepositoryClass, "\\"))) {
continue;
}

models.add(repositoryDefinition);
}
}

return models;
}

@NotNull
public static Collection<Pair<String, PsiElement>> getTables(@NotNull Project project) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ public char getKey() {
@Nullable
@Override
public PhpType getType(PsiElement e) {

if (DumbService.getInstance(e.getProject()).isDumb() || !Settings.getInstance(e.getProject()).pluginEnabled || !Settings.getInstance(e.getProject()).symfonyContainerTypeProvider) {
if (!Settings.getInstance(e.getProject()).pluginEnabled || !Settings.getInstance(e.getProject()).symfonyContainerTypeProvider) {
return null;
}

Expand Down
26 changes: 26 additions & 0 deletions src/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,32 @@ public static Set<Variable> getVariablesInScope(@NotNull PsiElement psiElement,
return MyVariableRecursiveElementVisitor.visit(psiElement, name);
}

/**
* Provide array key pattern. we need incomplete array key support, too.
*
* foo(['<caret>'])
* foo(['<caret>' => 'foobar'])
*/
@NotNull
public static PsiElementPattern.Capture<PsiElement> getParameterListArrayValuePattern() {
return PlatformPatterns.psiElement()
.withParent(PlatformPatterns.psiElement(StringLiteralExpression.class).withParent(
PlatformPatterns.or(
PlatformPatterns.psiElement().withElementType(PhpElementTypes.ARRAY_VALUE)
.withParent(PlatformPatterns.psiElement(ArrayCreationExpression.class)
.withParent(ParameterList.class)
),

PlatformPatterns.psiElement().withElementType(PhpElementTypes.ARRAY_KEY)
.withParent(PlatformPatterns.psiElement(ArrayHashElement.class)
.withParent(PlatformPatterns.psiElement(ArrayCreationExpression.class)
.withParent(ParameterList.class)
)
)
))
);
}

/**
* Visit and collect all variables in given scope
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class ServiceLineMarkerProviderTest extends SymfonyLightCodeInsightFixtur

public void setUp() throws Exception {
super.setUp();
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("SymfonyPhpReferenceContributor.php"));
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("ServiceLineMarkerProvider.php"));
}

public String getTestDataPath() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,14 @@ public class SymfonyPhpReferenceContributorTest extends SymfonyLightCodeInsightF

public void setUp() throws Exception {
super.setUp();
myFixture.copyFileToProject("SymfonyPhpReferenceContributor.php");
myFixture.copyFileToProject("services.xml");
myFixture.copyFileToProject("ServiceLineMarkerProvider.php");
}

public String getTestDataPath() {
return new File(this.getClass().getResource("fixtures").getFile()).getAbsolutePath();
}

/**
* @see fr.adrienbrault.idea.symfony2plugin.doctrine.ModelFieldReference
*/
public void testModelFieldReference() {
for (String s : new String[]{"findBy", "findOneBy"}) {
assertCompletionContains(PhpFileType.INSTANCE, "<?php" +
"/** @var $em \\Doctrine\\Common\\Persistence\\ObjectManager */\n" +
"$em->getRepository('Foo\\Bar')->" + s + "(['<caret>'])",
"phonenumbers", "email"
);

assertCompletionContains(PhpFileType.INSTANCE, "<?php" +
"/** @var $em \\Doctrine\\Common\\Persistence\\ObjectManager */\n" +
"$em->getRepository('Foo\\Bar')->" + s + "(['foo', '<caret>' => 'foo'])",
"phonenumbers", "email"
);

assertCompletionContains(PhpFileType.INSTANCE, "<?php" +
"/** @var $em \\Doctrine\\Common\\Persistence\\ObjectManager */\n" +
"$em->getRepository('Foo\\Bar')->" + s + "(['foo' => 'foo', '<caret>' => 'foo'])",
"phonenumbers", "email"
);

// migrate: @TODO: fr.adrienbrault.idea.symfony2plugin.doctrine.ModelFieldReference.multiResolve()
// add navigation testing
}
}

public void testThatPrivateServiceAreNotInCompletionListForContainerGet() {
assertCompletionContains(PhpFileType.INSTANCE, "<?php" +
"/** @var $c \\Symfony\\Component\\DependencyInjection\\ContainerInterface */\n" +
Expand Down
Loading

0 comments on commit 9791a8b

Please sign in to comment.