Skip to content

Commit ff1d295

Browse files
authored
Merge pull request #1702 from Haehnchen/feature/querybuilder-where
smarter Doctrine querybuilder "where" condition navigation to fields
2 parents 84ec688 + 973e837 commit ff1d295

File tree

4 files changed

+103
-9
lines changed

4 files changed

+103
-9
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/QueryBuilderGotoDeclarationHandler.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderPropertyAlias;
1212
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderRelation;
1313
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.util.MatcherUtil;
14+
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.util.QueryBuilderUtil;
1415
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
1516
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
1617
import org.apache.commons.lang.StringUtils;
18+
import org.jetbrains.annotations.NotNull;
1719
import org.jetbrains.annotations.Nullable;
1820

1921
import java.util.ArrayList;
@@ -27,26 +29,56 @@ public class QueryBuilderGotoDeclarationHandler implements GotoDeclarationHandle
2729

2830
@Nullable
2931
@Override
30-
public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) {
32+
public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset, Editor editor) {
3133

3234
if (!Symfony2ProjectComponent.isEnabled(psiElement) || !(psiElement.getContext() instanceof StringLiteralExpression)) {
3335
return new PsiElement[0];
3436
}
3537

3638
List<PsiElement> psiElements = new ArrayList<>();
3739

38-
attachPropertyGoto((StringLiteralExpression) psiElement.getContext(), psiElements);
39-
attachJoinGoto((StringLiteralExpression) psiElement.getContext(), psiElements);
40+
StringLiteralExpression context = (StringLiteralExpression) psiElement.getContext();
41+
attachPropertyGoto(context, psiElements);
42+
attachJoinGoto(context, psiElements);
43+
attachPartialGoto(context, psiElements, offset);
4044

4145
// $qb->expr()->in('')
42-
attachExprGoto((StringLiteralExpression) psiElement.getContext(), psiElements);
46+
attachExprGoto(context, psiElements);
4347

4448
// $qb->from('', '', '<foo>');
45-
attachFromIndexGoto((StringLiteralExpression) psiElement.getContext(), psiElements);
49+
attachFromIndexGoto(context, psiElements);
4650

4751
return psiElements.toArray(new PsiElement[psiElements.size()]);
4852
}
4953

54+
private void attachPartialGoto(@NotNull StringLiteralExpression psiElement, @NotNull List<PsiElement> targets, int offset) {
55+
MethodMatcher.MethodMatchParameter methodMatchParameter = MatcherUtil.matchWhere(psiElement);
56+
if(methodMatchParameter == null) {
57+
return;
58+
}
59+
60+
int calulatedOffset = offset - psiElement.getTextRange().getStartOffset();
61+
if (calulatedOffset < 0) {
62+
calulatedOffset = 0;
63+
}
64+
65+
String contents = psiElement.getContents();
66+
String fieldString = QueryBuilderUtil.getFieldString(contents, calulatedOffset);
67+
if (fieldString != null) {
68+
QueryBuilderMethodReferenceParser qb = QueryBuilderCompletionContributor.getQueryBuilderParser(methodMatchParameter.getMethodReference());
69+
if(qb == null) {
70+
return;
71+
}
72+
73+
QueryBuilderScopeContext collect = qb.collect();
74+
for(Map.Entry<String, QueryBuilderPropertyAlias> entry: collect.getPropertyAliasMap().entrySet()) {
75+
if(entry.getKey().equals(fieldString)) {
76+
targets.addAll(entry.getValue().getPsiTargets());
77+
}
78+
}
79+
}
80+
}
81+
5082
private void attachJoinGoto(StringLiteralExpression psiElement, List<PsiElement> targets) {
5183

5284
MethodMatcher.MethodMatchParameter methodMatchParameter = MatcherUtil.matchJoin(psiElement);
@@ -76,11 +108,8 @@ private void attachJoinGoto(StringLiteralExpression psiElement, List<PsiElement>
76108
if(phpClass != null) {
77109
targets.add(phpClass);
78110
}
79-
80111
}
81112
}
82-
83-
84113
}
85114

86115
private void attachPropertyGoto(StringLiteralExpression psiElement, List<PsiElement> targets) {

src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/MatcherUtil.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,12 @@ public static MethodMatcher.MethodMatchParameter matchJoin(PsiElement psiElement
6060
}
6161

6262

63-
63+
@Nullable
64+
public static MethodMatcher.MethodMatchParameter matchWhere(PsiElement psiElement) {
65+
return new MethodMatcher.StringParameterMatcher(psiElement, 0)
66+
.withSignature("\\Doctrine\\ORM\\QueryBuilder", "andWhere")
67+
.withSignature("\\Doctrine\\ORM\\QueryBuilder", "where")
68+
.withSignature("\\Doctrine\\ORM\\QueryBuilder", "orWhere")
69+
.match();
70+
}
6471
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/QueryBuilderUtil.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryTypeProvider;
44
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
56

67
import java.util.Collection;
78
import java.util.HashSet;
9+
import java.util.regex.Matcher;
10+
import java.util.regex.Pattern;
811

912
/**
1013
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -32,4 +35,35 @@ public static Collection<String> extractQueryBuilderRepositoryParameters(@NotNul
3235

3336
return results;
3437
}
38+
39+
/**
40+
* test "test.fooTe<caret>aa = aa"
41+
*/
42+
@Nullable
43+
public static String getFieldString(@NotNull String content, int offset) {
44+
if (offset > content.length()) {
45+
return null;
46+
}
47+
48+
String substring1 = content.substring(0, offset);
49+
Matcher matcherBefore = Pattern.compile("([\\w.]+)[\\s|>=<]?$").matcher(substring1);
50+
if (!matcherBefore.find()) {
51+
return null;
52+
}
53+
54+
String substring = content.substring(offset);
55+
Matcher matcherAfter = Pattern.compile("^[\\s|>=<]?([\\w.]+)").matcher(substring);
56+
if (!matcherAfter.find()) {
57+
return null;
58+
}
59+
60+
String field = matcherBefore.group(1) + matcherAfter.group(1);
61+
62+
// invalid field
63+
if (!field.contains(".")) {
64+
return null;
65+
}
66+
67+
return field;
68+
}
3569
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/doctrine/querybuilder/util/QueryBuilderUtilTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package fr.adrienbrault.idea.symfony2plugin.tests.doctrine.querybuilder.util;
22

3+
import com.intellij.openapi.util.Pair;
34
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.util.QueryBuilderUtil;
45
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
56

7+
import java.util.ArrayList;
68
import java.util.Collection;
79

810
/**
@@ -25,4 +27,26 @@ public void testExtractQueryBuilderRepositoryParametersForClassConstant() {
2527

2628
assertContainsElements(strings, "#K#C\\espend\\Doctrine\\ModelBundle\\Entity\\Car.class");
2729
}
30+
31+
public void testGetFieldString() {
32+
Collection<Pair<String, String>> items = new ArrayList<>() {{
33+
add(Pair.create("user.foo = :foo AND user.ba<caret>r AND bar.foo", "user.bar"));
34+
add(Pair.create("user.foo = :foo AND us<caret>er.bar AND bar.foo", "user.bar"));
35+
add(Pair.create("user.ba<caret>r>=", "user.bar"));
36+
add(Pair.create(">=user.ba<caret>r", "user.bar"));
37+
add(Pair.create("user.<caret>bar", "user.bar"));
38+
add(Pair.create("user<caret>.bar", "user.bar"));
39+
add(Pair.create("user.ba<caret>rÄ", "user.bar")); // nice usability but should this really match?
40+
add(Pair.create("us<caret>er.barÄ", "user.bar")); // nice usability but should this really match?
41+
add(Pair.create("us<caret>er", null));
42+
// add(Pair.create("user.bar<caret> AND", null)); // ???
43+
}};
44+
45+
for (Pair<String, String> item : items) {
46+
String content = item.getFirst();
47+
48+
String string = QueryBuilderUtil.getFieldString(content.replace("<caret>", ""), content.indexOf("<caret>"));
49+
assertEquals(item.getSecond(), string);
50+
}
51+
}
2852
}

0 commit comments

Comments
 (0)