2121use Doctrine \DBAL \Types \Type ;
2222use Illuminate \Console \Command ;
2323use Illuminate \Contracts \Database \Eloquent \CastsAttributes ;
24+ use Illuminate \Database \Eloquent \Casts \Attribute ;
2425use Illuminate \Database \Eloquent \Factories \Factory ;
2526use Illuminate \Database \Eloquent \Model ;
2627use Illuminate \Database \Eloquent \Relations \BelongsTo ;
3536use Illuminate \Database \Eloquent \Relations \MorphToMany ;
3637use Illuminate \Database \Eloquent \Relations \Relation ;
3738use Illuminate \Filesystem \Filesystem ;
39+ use Illuminate \Support \Collection ;
3840use Illuminate \Support \Str ;
3941use phpDocumentor \Reflection \Types \ContextFactory ;
4042use ReflectionClass ;
@@ -559,6 +561,9 @@ public function getPropertiesFromMethods($model)
559561 if ($ methods ) {
560562 sort ($ methods );
561563 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 );
562567 if (
563568 Str::startsWith ($ method , 'get ' ) && Str::endsWith (
564569 $ method ,
@@ -568,12 +573,25 @@ public function getPropertiesFromMethods($model)
568573 //Magic get<name>Attribute
569574 $ name = Str::snake (substr ($ method , 3 , -9 ));
570575 if (!empty ($ name )) {
571- $ reflection = new \ReflectionMethod ($ model , $ method );
572576 $ type = $ this ->getReturnType ($ reflection );
573577 $ type = $ this ->getTypeInModel ($ model , $ type );
574578 $ comment = $ this ->getCommentFromDocBlock ($ reflection );
575579 $ this ->setProperty ($ name , $ type , true , null , $ comment );
576580 }
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+ }
577595 } elseif (
578596 Str::startsWith ($ method , 'set ' ) && Str::endsWith (
579597 $ method ,
@@ -583,15 +601,13 @@ public function getPropertiesFromMethods($model)
583601 //Magic set<name>Attribute
584602 $ name = Str::snake (substr ($ method , 3 , -9 ));
585603 if (!empty ($ name )) {
586- $ reflection = new \ReflectionMethod ($ model , $ method );
587604 $ comment = $ this ->getCommentFromDocBlock ($ reflection );
588605 $ this ->setProperty ($ name , null , null , true , $ comment );
589606 }
590607 } elseif (Str::startsWith ($ method , 'scope ' ) && $ method !== 'scopeQuery ' ) {
591608 //Magic set<name>Attribute
592609 $ name = Str::camel (substr ($ method , 5 ));
593610 if (!empty ($ name )) {
594- $ reflection = new \ReflectionMethod ($ model , $ method );
595611 $ comment = $ this ->getCommentFromDocBlock ($ reflection );
596612 $ args = $ this ->getParameters ($ reflection );
597613 //Remove the first ($query) argument
@@ -622,8 +638,6 @@ public function getPropertiesFromMethods($model)
622638 && !Str::startsWith ($ method , 'get ' )
623639 ) {
624640 //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
625- $ reflection = new \ReflectionMethod ($ model , $ method );
626-
627641 if ($ returnType = $ reflection ->getReturnType ()) {
628642 $ type = $ returnType instanceof ReflectionNamedType
629643 ? $ returnType ->getName ()
@@ -1056,6 +1070,36 @@ protected function hasCamelCaseModelProperties()
10561070 return $ this ->laravel ['config ' ]->get ('ide-helper.model_camel_case_properties ' , false );
10571071 }
10581072
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+
10591103 protected function getReturnType (\ReflectionMethod $ reflection ): ?string
10601104 {
10611105 $ type = $ this ->getReturnTypeFromDocBlock ($ reflection );
0 commit comments