21
21
use Doctrine \DBAL \Types \Type ;
22
22
use Illuminate \Console \Command ;
23
23
use Illuminate \Contracts \Database \Eloquent \CastsAttributes ;
24
+ use Illuminate \Database \Eloquent \Casts \Attribute ;
24
25
use Illuminate \Database \Eloquent \Factories \Factory ;
25
26
use Illuminate \Database \Eloquent \Model ;
26
27
use Illuminate \Database \Eloquent \Relations \BelongsTo ;
35
36
use Illuminate \Database \Eloquent \Relations \MorphToMany ;
36
37
use Illuminate \Database \Eloquent \Relations \Relation ;
37
38
use Illuminate \Filesystem \Filesystem ;
39
+ use Illuminate \Support \Collection ;
38
40
use Illuminate \Support \Str ;
39
41
use phpDocumentor \Reflection \Types \ContextFactory ;
40
42
use ReflectionClass ;
@@ -559,6 +561,9 @@ public function getPropertiesFromMethods($model)
559
561
if ($ methods ) {
560
562
sort ($ methods );
561
563
foreach ($ methods as $ method ) {
564
+ $ reflection = new \ReflectionMethod ($ model , $ method );
565
+ $ type = $ this ->getReturnType ($ reflection );
566
+ $ isAttribute = is_a ($ type , '\Illuminate\Database\Eloquent\Casts\Attribute ' , true );
562
567
if (
563
568
Str::startsWith ($ method , 'get ' ) && Str::endsWith (
564
569
$ method ,
@@ -568,12 +573,25 @@ public function getPropertiesFromMethods($model)
568
573
//Magic get<name>Attribute
569
574
$ name = Str::snake (substr ($ method , 3 , -9 ));
570
575
if (!empty ($ name )) {
571
- $ reflection = new \ReflectionMethod ($ model , $ method );
572
576
$ type = $ this ->getReturnType ($ reflection );
573
577
$ type = $ this ->getTypeInModel ($ model , $ type );
574
578
$ comment = $ this ->getCommentFromDocBlock ($ reflection );
575
579
$ this ->setProperty ($ name , $ type , true , null , $ comment );
576
580
}
581
+ } elseif ($ isAttribute ) {
582
+ $ name = Str::snake ($ method );
583
+ $ types = $ this ->getAttributeReturnType ($ model , $ method );
584
+
585
+ if ($ types ->has ('get ' )) {
586
+ $ type = $ this ->getTypeInModel ($ model , $ types ['get ' ]);
587
+ $ comment = $ this ->getCommentFromDocBlock ($ reflection );
588
+ $ this ->setProperty ($ name , $ type , true , null , $ comment );
589
+ }
590
+
591
+ if ($ types ->has ('set ' )) {
592
+ $ comment = $ this ->getCommentFromDocBlock ($ reflection );
593
+ $ this ->setProperty ($ name , null , null , true , $ comment );
594
+ }
577
595
} elseif (
578
596
Str::startsWith ($ method , 'set ' ) && Str::endsWith (
579
597
$ method ,
@@ -583,15 +601,13 @@ public function getPropertiesFromMethods($model)
583
601
//Magic set<name>Attribute
584
602
$ name = Str::snake (substr ($ method , 3 , -9 ));
585
603
if (!empty ($ name )) {
586
- $ reflection = new \ReflectionMethod ($ model , $ method );
587
604
$ comment = $ this ->getCommentFromDocBlock ($ reflection );
588
605
$ this ->setProperty ($ name , null , null , true , $ comment );
589
606
}
590
607
} elseif (Str::startsWith ($ method , 'scope ' ) && $ method !== 'scopeQuery ' ) {
591
608
//Magic set<name>Attribute
592
609
$ name = Str::camel (substr ($ method , 5 ));
593
610
if (!empty ($ name )) {
594
- $ reflection = new \ReflectionMethod ($ model , $ method );
595
611
$ comment = $ this ->getCommentFromDocBlock ($ reflection );
596
612
$ args = $ this ->getParameters ($ reflection );
597
613
//Remove the first ($query) argument
@@ -622,8 +638,6 @@ public function getPropertiesFromMethods($model)
622
638
&& !Str::startsWith ($ method , 'get ' )
623
639
) {
624
640
//Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
625
- $ reflection = new \ReflectionMethod ($ model , $ method );
626
-
627
641
if ($ returnType = $ reflection ->getReturnType ()) {
628
642
$ type = $ returnType instanceof ReflectionNamedType
629
643
? $ returnType ->getName ()
@@ -1056,6 +1070,36 @@ protected function hasCamelCaseModelProperties()
1056
1070
return $ this ->laravel ['config ' ]->get ('ide-helper.model_camel_case_properties ' , false );
1057
1071
}
1058
1072
1073
+ protected function getAttributeReturnType (Model $ model , string $ method ): Collection
1074
+ {
1075
+ /** @var Attribute $attribute */
1076
+ $ attribute = $ model ->{$ method }();
1077
+
1078
+ return collect ([
1079
+ 'get ' => $ attribute ->get ? optional (new \ReflectionFunction ($ attribute ->get ))->getReturnType () : null ,
1080
+ 'set ' => $ attribute ->set ? optional (new \ReflectionFunction ($ attribute ->set ))->getReturnType () : null ,
1081
+ ])
1082
+ ->filter ()
1083
+ ->map (function ($ type ) {
1084
+ if ($ type instanceof \ReflectionUnionType) {
1085
+ $ types =collect ($ type ->getTypes ())
1086
+ /** @var ReflectionType $reflectionType */
1087
+ ->map (function ($ reflectionType ) {
1088
+ return collect ($ this ->extractReflectionTypes ($ reflectionType ));
1089
+ })
1090
+ ->flatten ();
1091
+ } else {
1092
+ $ types = collect ($ this ->extractReflectionTypes ($ type ));
1093
+ }
1094
+
1095
+ if ($ type ->allowsNull ()) {
1096
+ $ types ->push ('null ' );
1097
+ }
1098
+
1099
+ return $ types ->join ('| ' );
1100
+ });
1101
+ }
1102
+
1059
1103
protected function getReturnType (\ReflectionMethod $ reflection ): ?string
1060
1104
{
1061
1105
$ type = $ this ->getReturnTypeFromDocBlock ($ reflection );
0 commit comments