Skip to content

Commit ece107d

Browse files
committed
Detect ObjectStorage contents and add it to the field types on typed getters
Closes: #158
1 parent 1bdcde3 commit ece107d

File tree

4 files changed

+154
-13
lines changed

4 files changed

+154
-13
lines changed

src/main/java/com/cedricziel/idea/typo3/extbase/persistence/ExtbaseModelCollectionReturnTypeProvider.java

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
import com.jetbrains.php.PhpIndex;
88
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
99
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocParamTag;
10-
import com.jetbrains.php.lang.psi.elements.Field;
11-
import com.jetbrains.php.lang.psi.elements.PhpClass;
12-
import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
10+
import com.jetbrains.php.lang.psi.elements.*;
1311
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
1412
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3;
1513
import org.jetbrains.annotations.Nullable;
@@ -35,27 +33,32 @@ public PhpType getType(PsiElement psiElement) {
3533
return null;
3634
}
3735

38-
if (!(psiElement instanceof Field)) {
36+
if (!(psiElement instanceof Field) && !isGetter(psiElement)) {
3937
return null;
4038
}
4139

42-
PhpClass containingClass = ((Field) psiElement).getContainingClass();
43-
if (containingClass == null) {
40+
if (!isEntityClass(psiElement)) {
4441
return null;
4542
}
4643

47-
Collection<PhpClass> classesByFQN = PhpIndex.getInstance(psiElement.getProject()).getClassesByFQN(TYPO3_CMS_EXTBASE_DOMAIN_OBJECT_ABSTRACT_ENTITY);
48-
if (classesByFQN.isEmpty()) {
49-
return null;
50-
}
44+
return extractReturnType(psiElement);
45+
}
5146

52-
PhpClass abstractEntityClass = classesByFQN.iterator().next();
47+
private PhpType extractReturnType(PsiElement psiElement) {
48+
Field field;
49+
if (psiElement instanceof MethodReference) {
50+
field = extractFieldFromGetter((MethodReference) psiElement);
51+
} else if (psiElement instanceof Method) {
52+
field = extractFieldFromGetter((Method) psiElement);
53+
} else {
54+
field = ((Field) psiElement);
55+
}
5356

54-
if (!PhpClassHierarchyUtils.isSuperClass(abstractEntityClass, containingClass, true)) {
57+
if (field == null) {
5558
return null;
5659
}
5760

58-
PhpDocComment docComment = ((Field) psiElement).getDocComment();
61+
PhpDocComment docComment = field.getDocComment();
5962
if (docComment == null) {
6063
return null;
6164
}
@@ -82,6 +85,62 @@ public PhpType getType(PsiElement psiElement) {
8285
return phpType;
8386
}
8487

88+
private Field extractFieldFromGetter(MethodReference methodReference) {
89+
String substring = methodReference.getName().substring(2);
90+
char[] cArr = substring.toCharArray();
91+
cArr[0] = Character.toLowerCase(cArr[0]);
92+
93+
String propertyName = new String(cArr);
94+
95+
PsiElement method = methodReference.resolve();
96+
if (!(method instanceof Method)) {
97+
return null;
98+
}
99+
100+
PhpClass containingClass = ((Method) method).getContainingClass();
101+
102+
if (containingClass == null) {
103+
return null;
104+
}
105+
106+
return containingClass.findFieldByName(propertyName, true);
107+
}
108+
109+
private Field extractFieldFromGetter(Method method) {
110+
String substring = method.getName().substring(3);
111+
char[] cArr = substring.toCharArray();
112+
cArr[0] = Character.toLowerCase(cArr[0]);
113+
114+
String propertyName = new String(cArr);
115+
116+
PhpClass containingClass = method.getContainingClass();
117+
118+
if (containingClass == null) {
119+
return null;
120+
}
121+
122+
return containingClass.findFieldByName(propertyName, false);
123+
}
124+
125+
private boolean isEntityClass(PsiElement psiElement) {
126+
PhpClass containingClass = ((PhpClassMember) psiElement).getContainingClass();
127+
if (containingClass == null) {
128+
return false;
129+
}
130+
131+
Collection<PhpClass> classesByFQN = PhpIndex.getInstance(psiElement.getProject()).getClassesByFQN(TYPO3_CMS_EXTBASE_DOMAIN_OBJECT_ABSTRACT_ENTITY);
132+
if (classesByFQN.isEmpty()) {
133+
return false;
134+
}
135+
136+
PhpClass abstractEntityClass = classesByFQN.iterator().next();
137+
return PhpClassHierarchyUtils.isSuperClass(abstractEntityClass, containingClass, true);
138+
}
139+
140+
private boolean isGetter(PsiElement psiElement) {
141+
return (psiElement instanceof Method) && ((Method) psiElement).getName().startsWith("get");
142+
}
143+
85144
@Override
86145
public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) {
87146
return PhpIndex.getInstance(project).getBySignature(expression);

src/test/java/com/cedricziel/idea/typo3/extbase/persistence/ExtbaseModelCollectionReturnTypeProviderTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.intellij.psi.PsiElement;
44
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
55
import com.jetbrains.php.lang.psi.elements.Field;
6+
import com.jetbrains.php.lang.psi.elements.Method;
67

78
import java.util.Set;
89

@@ -23,4 +24,28 @@ public void testResolvesObjectStoragePropertiesToObjectTypes() {
2324
Set<String> types = ((Field) elementAtCaret).getInferredType().getTypes();
2425
assertTrue(types.contains("\\My\\Extension\\Domain\\Model\\Book[]"));
2526
}
27+
28+
public void testResolvesObjectStoragePropertiesToObjectTypesOnGetters() {
29+
myFixture.copyFileToProject("PersistenceMocks.php");
30+
myFixture.configureByFile("MethodTypeProvider.php");
31+
32+
PsiElement elementAtCaret = myFixture.getElementAtCaret();
33+
34+
assertInstanceOf(elementAtCaret, Method.class);
35+
36+
Set<String> types = ((Method) elementAtCaret).getInferredType().getTypes();
37+
assertTrue(types.contains("\\My\\Extension\\Domain\\Model\\Book[]"));
38+
}
39+
40+
public void testResolvesObjectStoragePropertiesToObjectTypesOnOutsideCalls() {
41+
myFixture.copyFileToProject("PersistenceMocks.php");
42+
myFixture.configureByFile("MethodReferenceTypeProvider.php");
43+
44+
PsiElement elementAtCaret = myFixture.getElementAtCaret();
45+
46+
assertInstanceOf(elementAtCaret, Method.class);
47+
48+
Set<String> types = ((Method) elementAtCaret).getType().getTypes();
49+
assertTrue(types.contains("\\My\\Extension\\Domain\\Model\\Book[]"));
50+
}
2651
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace MyExt {
4+
5+
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
6+
7+
class MethodReferenceTypeProvider extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
8+
{
9+
/**
10+
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\My\Extension\Domain\Model\Book>
11+
*/
12+
protected $book;
13+
14+
public function getBook() : ObjectStorage
15+
{
16+
return $this->book;
17+
}
18+
}
19+
}
20+
21+
namespace Bar {
22+
23+
use MyExt\MethodReferenceTypeProvider;
24+
25+
class Foo {
26+
public function getFoo()
27+
{
28+
$methodTypeProvider = new MethodReferenceTypeProvider();
29+
$methodTypeProvider->get<caret>Book();
30+
}
31+
}
32+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace MyExt {
4+
5+
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
6+
7+
class MethodTypeProvider extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
8+
{
9+
/**
10+
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\My\Extension\Domain\Model\Book>
11+
*/
12+
protected $book;
13+
14+
public function getBook() : ObjectStorage
15+
{
16+
return $this->book;
17+
}
18+
19+
public function foo()
20+
{
21+
return $this-><caret>getBook();
22+
}
23+
}
24+
25+
}

0 commit comments

Comments
 (0)