Skip to content

Commit 8144d2c

Browse files
committed
#2176 support route names on class level with __invoke method
1 parent 107fede commit 8144d2c

File tree

4 files changed

+80
-26
lines changed

4 files changed

+80
-26
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/RoutesStubIndex.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public DataIndexer<String, StubIndexedRoute, FileContent> getIndexer() {
7878
}
7979
}
8080
} else if(psiFile instanceof PhpFile) {
81-
// annotations: @Route()
81+
// @Route(), #[Route]
8282
if(!isValidForIndex(inputData, psiFile)) {
8383
return map;
8484
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/visitor/AnnotationRouteElementVisitor.java

+47-25
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,19 @@ public void visitFile(@NotNull PhpClass phpClass) {
5959

6060
PhpAttributesList childOfType = PsiTreeUtil.getChildOfType(method, PhpAttributesList.class);
6161
if (childOfType != null) {
62-
visitPhpAttributesList(childOfType);
62+
visitPhpAttributesList(childOfType, method, phpClass, false);
63+
}
64+
}
65+
66+
Method invoke = phpClass.findOwnMethodByName("__invoke");
67+
if (invoke != null) {
68+
PhpAttributesList childOfType = PsiTreeUtil.getChildOfType(phpClass, PhpAttributesList.class);
69+
if (childOfType != null) {
70+
visitPhpAttributesList(childOfType, invoke, phpClass, true);
6371
}
6472
}
6573
}
74+
6675
private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) {
6776

6877
// "@var" and user non-related tags don't need an action
@@ -132,32 +141,33 @@ private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) {
132141
}
133142
}
134143

135-
private void visitPhpAttributesList(@NotNull PhpAttributesList phpAttributesList) {
144+
private void visitPhpAttributesList(@NotNull PhpAttributesList phpAttributesList, @NotNull Method method, @NotNull PhpClass phpClass, boolean classLevel) {
136145
PsiElement parent = phpAttributesList.getParent();
137146

138147
// prefix on class scope
139148
String routeNamePrefix = "";
140149
String routePathPrefix = "";
141-
if (parent instanceof Method) {
142-
PhpClass containingClass = ((Method) parent).getContainingClass();
143-
if (containingClass != null) {
144-
for (PhpAttribute attribute : containingClass.getAttributes()) {
145-
String fqn = attribute.getFQN();
146-
if(fqn == null || !RouteHelper.isRouteClassAnnotation(fqn)) {
147-
continue;
148-
}
149150

150-
String nameAttribute = PhpPsiAttributesUtil.getAttributeValueByNameAsString(attribute, 1, "name");
151-
if (nameAttribute != null) {
152-
routeNamePrefix = nameAttribute;
153-
}
151+
for (PhpAttribute attribute : phpClass.getAttributes()) {
152+
String fqn = attribute.getFQN();
153+
if(fqn == null || !RouteHelper.isRouteClassAnnotation(fqn)) {
154+
continue;
155+
}
154156

155-
String pathAttribute = PhpPsiAttributesUtil.getAttributeValueByNameAsStringWithDefaultParameterFallback(attribute, "path");;
156-
if (pathAttribute != null) {
157-
routePathPrefix = pathAttribute;
158-
}
159-
}
157+
String nameAttribute = PhpPsiAttributesUtil.getAttributeValueByNameAsString(attribute, 1, "name");
158+
if (nameAttribute != null) {
159+
routeNamePrefix = nameAttribute;
160160
}
161+
162+
String pathAttribute = PhpPsiAttributesUtil.getAttributeValueByNameAsStringWithDefaultParameterFallback(attribute, "path");;
163+
if (pathAttribute != null) {
164+
routePathPrefix = pathAttribute;
165+
}
166+
}
167+
168+
if (classLevel) {
169+
routePathPrefix = "";
170+
routeNamePrefix = "";
161171
}
162172

163173
for (PhpAttribute attribute : phpAttributesList.getAttributes()) {
@@ -168,13 +178,11 @@ private void visitPhpAttributesList(@NotNull PhpAttributesList phpAttributesList
168178

169179
String nameAttribute = PhpPsiAttributesUtil.getAttributeValueByNameAsString(attribute, 1, "name");
170180

171-
String routeName = null;
181+
String routeName;
172182
if (nameAttribute != null) {
173183
routeName = nameAttribute;
174184
} else {
175-
if (parent instanceof Method) {
176-
routeName = AnnotationBackportUtil.getRouteByMethod((Method) parent);
177-
}
185+
routeName = AnnotationBackportUtil.getRouteByMethod(method);
178186
}
179187

180188
if (routeName == null) {
@@ -183,8 +191,10 @@ private void visitPhpAttributesList(@NotNull PhpAttributesList phpAttributesList
183191

184192
StubIndexedRoute route = new StubIndexedRoute(routeNamePrefix + routeName);
185193

186-
if (parent instanceof Method) {
187-
route.setController(getController((Method) parent));
194+
if (classLevel) {
195+
route.setController(getController(phpClass));
196+
} else {
197+
route.setController(getController(method));
188198
}
189199

190200
// find path "#[Route('/attributesWithoutName')]" or "#[Route(path: '/attributesWithoutName')]"
@@ -304,6 +314,18 @@ private String getController(@NotNull Method method) {
304314
);
305315
}
306316

317+
private String getController(@NotNull PhpClass phpClass) {
318+
if(phpClass.findOwnMethodByName("__invoke") == null) {
319+
return null;
320+
}
321+
322+
return String.format(
323+
"%s::%s",
324+
StringUtils.stripStart(phpClass.getFQN(), "\\"),
325+
"__invoke"
326+
);
327+
}
328+
307329
@Nullable
308330
private String getClassRoutePattern(@NotNull PhpDocTag phpDocTag) {
309331
PhpClass phpClass = PsiTreeUtil.getParentOfType(phpDocTag, PhpClass.class);

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTest.java

+10
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,16 @@ public void testThatPhp8AttributesMethodsAreInIndex() {
198198
assertEquals("/foo-attributes/edit-not-named/{id}", route9.getPath());
199199
}
200200

201+
public void testThatPhp8AttributesViaClassInvokeMethodsAreInIndex() {
202+
RouteInterface route = getFirstValue("invoke_route_attribute");
203+
assertEquals("/foo-attributes", route.getPath());
204+
assertEquals("AttributeInvoke\\MyController::__invoke", route.getController());
205+
206+
RouteInterface route2 = getFirstValue("attributeinvoke_noname__invoke");
207+
assertEquals("/foo-attributes/no-name", route2.getPath());
208+
assertEquals("AttributeInvoke\\NoNameController::__invoke", route2.getController());
209+
}
210+
201211
@NotNull
202212
private RouteInterface getFirstValue(@NotNull String key) {
203213
return FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, key, GlobalSearchScope.allScope(getProject())).get(0);

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures/RoutesStubIndex.php

+22
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,28 @@ public function emptyAttribute()
265265
}
266266
}
267267

268+
269+
namespace AttributeInvoke
270+
{
271+
use Symfony\Component\Routing\Annotation\Route;
272+
273+
#[Route(path: '/foo-attributes', name: 'invoke_route_attribute')]
274+
class MyController
275+
{
276+
public function __invoke()
277+
{
278+
}
279+
}
280+
281+
#[Route(path: '/foo-attributes/no-name')]
282+
class NoNameController
283+
{
284+
public function __invoke()
285+
{
286+
}
287+
}
288+
}
289+
268290
namespace Symfony\Component\Routing\Annotation
269291
{
270292
class Route

0 commit comments

Comments
 (0)