Add support for non type-hinted attribute accessors with no backed property (#1411)

* Read Attribute type from parameter

* Update CHANGELOG.md

* Update ModelsCommand.php

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
Co-authored-by: Barry vd. Heuvel <[email protected]>
This commit is contained in:
Hans van Luttikhuizen-Ross
2024-02-08 09:39:10 +01:00
committed by GitHub
co-authored by Barry vd. Heuvel Barry vd. Heuvel
parent 4751420c9e
commit f12d933ab1
4 changed files with 194 additions and 29 deletions
+34 -26
View File
@@ -620,10 +620,7 @@ class ModelsCommand extends Command
// methods that resemble mutators but aren't.
$reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) {
return !$methodReflection->isPrivate() && !(
in_array(
\Illuminate\Database\Eloquent\Concerns\HasAttributes::class,
$methodReflection->getDeclaringClass()->getTraitNames()
) && (
$methodReflection->getDeclaringClass()->getName() === \Illuminate\Database\Eloquent\Model::class && (
$methodReflection->getName() === 'setClassCastableAttribute' ||
$methodReflection->getName() === 'setEnumCastableAttribute'
)
@@ -649,18 +646,15 @@ class ModelsCommand extends Command
$this->setProperty($name, $type, true, null, $comment);
}
} elseif ($isAttribute) {
$name = Str::snake($method);
$types = $this->getAttributeReturnType($model, $reflection);
$comment = $this->getCommentFromDocBlock($reflection);
if ($types->has('get')) {
$type = $this->getTypeInModel($model, $types['get']);
$this->setProperty($name, $type, true, null, $comment);
}
if ($types->has('set')) {
$this->setProperty($name, null, null, true, $comment);
}
$types = $this->getAttributeTypes($model, $reflection);
$type = $this->getTypeInModel($model, $types->get('get') ?: $types->get('set')) ?: null;
$this->setProperty(
Str::snake($method),
$type,
$types->has('get'),
$types->has('set'),
$this->getCommentFromDocBlock($reflection)
);
} elseif (
Str::startsWith($method, 'set') && Str::endsWith(
$method,
@@ -1192,7 +1186,10 @@ class ModelsCommand extends Command
return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false);
}
protected function getAttributeReturnType(Model $model, \ReflectionMethod $reflectionMethod): Collection
/**
* @psalm-suppress NoValue
*/
protected function getAttributeTypes(Model $model, \ReflectionMethod $reflectionMethod): Collection
{
// Private/protected ReflectionMethods require setAccessible prior to PHP 8.1
$reflectionMethod->setAccessible(true);
@@ -1200,13 +1197,25 @@ class ModelsCommand extends Command
/** @var Attribute $attribute */
$attribute = $reflectionMethod->invoke($model);
return collect([
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,
'set' => $attribute->set ? optional(new \ReflectionFunction($attribute->set))->getReturnType() : null,
])
->filter()
$methods = new Collection();
if ($attribute->get) {
$methods['get'] = optional(new \ReflectionFunction($attribute->get))->getReturnType();
}
if ($attribute->set) {
$function = optional(new \ReflectionFunction($attribute->set));
if ($function->getNumberOfParameters() === 0) {
$methods['set'] = null;
} else {
$methods['set'] = $function->getParameters()[0]->getType();
}
}
return $methods
->map(function ($type) {
if ($type instanceof \ReflectionUnionType) {
if ($type === null) {
$types = collect([]);
} elseif ($type instanceof \ReflectionUnionType) {
$types = collect($type->getTypes())
/** @var ReflectionType $reflectionType */
->map(function ($reflectionType) {
@@ -1217,7 +1226,7 @@ class ModelsCommand extends Command
$types = collect($this->extractReflectionTypes($type));
}
if ($type->allowsNull()) {
if ($type && $type->allowsNull()) {
$types->push('null');
}
@@ -1467,8 +1476,7 @@ class ModelsCommand extends Command
{
$reflection = $model instanceof ReflectionClass
? $model
: new ReflectionObject($model)
;
: new ReflectionObject($model);
$className = trim($className, '\\');
$writingToExternalFile = !$this->write || $this->write_mixin;