5
5
namespace Larastan \Larastan \Properties ;
6
6
7
7
use Illuminate \Database \Eloquent \Model ;
8
- use Illuminate \Database \Eloquent \Relations \BelongsToMany ;
9
- use Illuminate \Database \Eloquent \Relations \HasMany ;
10
- use Illuminate \Database \Eloquent \Relations \HasManyThrough ;
11
- use Illuminate \Database \Eloquent \Relations \HasOneThrough ;
12
- use Illuminate \Database \Eloquent \Relations \MorphMany ;
13
- use Illuminate \Database \Eloquent \Relations \MorphTo ;
14
- use Illuminate \Database \Eloquent \Relations \MorphToMany ;
15
8
use Illuminate \Database \Eloquent \Relations \Relation ;
16
9
use Illuminate \Support \Str ;
17
10
use Larastan \Larastan \Concerns ;
18
11
use Larastan \Larastan \Reflection \ReflectionHelper ;
19
12
use Larastan \Larastan \Support \CollectionHelper ;
20
13
use PHPStan \Analyser \OutOfClassScope ;
21
14
use PHPStan \Reflection \ClassReflection ;
22
- use PHPStan \Reflection \MissingMethodFromReflectionException ;
23
15
use PHPStan \Reflection \PropertiesClassReflectionExtension ;
24
16
use PHPStan \Reflection \PropertyReflection ;
25
- use PHPStan \ShouldNotHappenException ;
26
- use PHPStan \Type \ErrorType ;
27
- use PHPStan \Type \Generic \GenericObjectType ;
28
17
use PHPStan \Type \IntegerRangeType ;
29
18
use PHPStan \Type \IntersectionType ;
30
- use PHPStan \Type \MixedType ;
31
19
use PHPStan \Type \NeverType ;
32
- use PHPStan \Type \NullType ;
33
20
use PHPStan \Type \ObjectType ;
34
21
use PHPStan \Type \Type ;
35
- use PHPStan \Type \TypeCombinator ;
36
22
use PHPStan \Type \TypeTraverser ;
37
23
use PHPStan \Type \UnionType ;
38
24
@@ -66,18 +52,12 @@ public function hasProperty(ClassReflection $classReflection, string $propertyNa
66
52
}
67
53
68
54
foreach ($ methodNames as $ methodName ) {
69
- $ hasNativeMethod = $ classReflection ->hasNativeMethod ($ methodName );
70
-
71
- if (! $ hasNativeMethod ) {
55
+ if (! $ classReflection ->hasNativeMethod ($ methodName )) {
72
56
continue ;
73
57
}
74
58
75
59
$ returnType = $ classReflection ->getNativeMethod ($ methodName )->getVariants ()[0 ]->getReturnType ();
76
60
77
- if ($ returnType ->getTemplateType (Relation::class, 'TRelatedModel ' ) instanceof ErrorType) {
78
- continue ;
79
- }
80
-
81
61
if ((new ObjectType (Relation::class))->isSuperTypeOf ($ returnType )->yes ()) {
82
62
return true ;
83
63
}
@@ -86,92 +66,30 @@ public function hasProperty(ClassReflection $classReflection, string $propertyNa
86
66
return false ;
87
67
}
88
68
89
- /**
90
- * @throws ShouldNotHappenException
91
- * @throws MissingMethodFromReflectionException
92
- */
93
69
public function getProperty (ClassReflection $ classReflection , string $ propertyName ): PropertyReflection
94
70
{
95
71
if (str_ends_with ($ propertyName , '_count ' )) {
96
72
return new ModelProperty ($ classReflection , IntegerRangeType::createAllGreaterThanOrEqualTo (0 ), new NeverType (), false );
97
73
}
98
74
99
- $ method = $ classReflection ->getMethod ($ propertyName , new OutOfClassScope ());
100
-
101
- $ returnType = $ method ->getVariants ()[0 ]->getReturnType ();
102
-
103
- $ relatedModel = $ returnType ->getTemplateType (Relation::class, 'TRelatedModel ' );
104
-
105
- if ($ relatedModel ->getObjectClassNames () === []) {
106
- $ relatedModelClassNames = [Model::class];
107
- } else {
108
- $ relatedModelClassNames = $ relatedModel ->getObjectClassNames ();
109
- }
75
+ $ returnType = $ classReflection ->getMethod ($ propertyName , new OutOfClassScope ())
76
+ ->getVariants ()[0 ]
77
+ ->getReturnType ();
110
78
111
- $ relationType = TypeTraverser::map ($ returnType , function (Type $ type , callable $ traverse ) use ( $ relatedModelClassNames , $ relatedModel ) {
79
+ $ relationType = TypeTraverser::map ($ returnType , static function (Type $ type , callable $ traverse ): Type {
112
80
if ($ type instanceof UnionType || $ type instanceof IntersectionType) {
113
81
return $ traverse ($ type );
114
82
}
115
83
116
- if ($ type ->getObjectClassNames () === []) {
117
- return $ traverse ($ type );
118
- }
119
-
120
- if ($ type instanceof GenericObjectType) {
121
- $ relatedModel = $ type ->getTypes ()[0 ];
122
- $ relatedModelClassNames = $ relatedModel ->getObjectClassNames ();
123
- }
124
-
125
- if (
126
- (new ObjectType (BelongsToMany::class))->isSuperTypeOf ($ type )->yes ()
127
- || (new ObjectType (HasMany::class))->isSuperTypeOf ($ type )->yes ()
128
- || (
129
- (new ObjectType (HasManyThrough::class))->isSuperTypeOf ($ type )->yes ()
130
- // HasOneThrough extends HasManyThrough
131
- && ! (new ObjectType (HasOneThrough::class))->isSuperTypeOf ($ type )->yes ()
132
- )
133
- || (new ObjectType (MorphMany::class))->isSuperTypeOf ($ type )->yes ()
134
- || (new ObjectType (MorphToMany::class))->isSuperTypeOf ($ type )->yes ()
135
- || Str::contains ($ type ->getObjectClassNames ()[0 ], 'Many ' ) // fallback
136
- ) {
137
- $ types = [];
138
-
139
- foreach ($ relatedModelClassNames as $ relatedModelClassName ) {
140
- $ types [] = $ this ->collectionHelper ->determineCollectionClass ($ relatedModelClassName );
141
- }
142
-
143
- if ($ types !== []) {
144
- return TypeCombinator::union (...$ types );
145
- }
146
- }
147
-
148
- if (
149
- (new ObjectType (MorphTo::class))->isSuperTypeOf ($ type )->yes ()
150
- || Str::endsWith ($ type ->getObjectClassNames ()[0 ], 'MorphTo ' ) // fallback
151
- ) {
152
- // There was no generic type, or it was just Model
153
- // so we will return mixed to avoid errors.
154
- if ($ relatedModel ->getObjectClassNames ()[0 ] === Model::class) {
155
- return new MixedType ();
156
- }
157
-
158
- $ types = [];
159
-
160
- foreach ($ relatedModelClassNames as $ relatedModelClassName ) {
161
- $ types [] = new ObjectType ($ relatedModelClassName );
162
- }
163
-
164
- if ($ types !== []) {
165
- return TypeCombinator::union (...$ types );
166
- }
84
+ if (! (new ObjectType (Relation::class))->isSuperTypeOf ($ type )->yes ()) {
85
+ return $ type ;
167
86
}
168
87
169
- return new UnionType ([
170
- $ relatedModel ,
171
- new NullType (),
172
- ]);
88
+ return $ type ->getTemplateType (Relation::class, 'TResult ' );
173
89
});
174
90
91
+ $ relationType = $ this ->collectionHelper ->replaceCollectionsInType ($ relationType );
92
+
175
93
return new ModelProperty ($ classReflection , $ relationType , new NeverType (), false );
176
94
}
177
95
}
0 commit comments