mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-17 17:47:13 +00:00
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:
co-authored by
Barry vd. Heuvel
Barry vd. Heuvel
parent
4751420c9e
commit
f12d933ab1
+2
-1
@@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Add support for enum default arguments using enum cases. [#1464 / d8vjork](https://github.com/barryvdh/laravel-ide-helper/pull/1464)
|
||||
- Add support for real-time facades in the helper file. [#1455 / filipac](https://github.com/barryvdh/laravel-ide-helper/pull/1455)
|
||||
- Add support for relations with composite keys. [#1479 / calebdw](https://github.com/barryvdh/laravel-ide-helper/pull/1479)
|
||||
- Add support for attribute accessors with no backing field or type hinting [#1411 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1411).
|
||||
|
||||
2024-02-05, 2.14.0
|
||||
------------------
|
||||
@@ -24,12 +25,12 @@ All notable changes to this project will be documented in this file.
|
||||
- Refactor resolving of null information for custom casted attribute types [#1330 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1330)
|
||||
|
||||
### Fixed
|
||||
- Add support for attribute accessors marked as protected. [#1339 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1339)
|
||||
- Catch exceptions when loading aliases [#1465 / dongm2ez](https://github.com/barryvdh/laravel-ide-helper/pull/1465)
|
||||
|
||||
### Added
|
||||
- Add support for nikic/php-parser 5 (next to 4) [#1502 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1502)
|
||||
- Add support for `immutable_date:*` and `immutable_datetime:*` casts. [#1380 / thekonz](https://github.com/barryvdh/laravel-ide-helper/pull/1380)
|
||||
- Add support for attribute accessors marked as protected. [#1339 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1339)
|
||||
|
||||
2023-02-04, 2.13.0
|
||||
------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Simple extends Model
|
||||
{
|
||||
// With a backed property
|
||||
protected function name(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
@@ -16,11 +17,84 @@ class Simple extends Model
|
||||
return $name;
|
||||
},
|
||||
function (?string $name): ?string {
|
||||
return $name === null ? null : ucfirst($name);
|
||||
return $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Without backed properties
|
||||
|
||||
protected function typeHintedGetAndSet(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
function (): ?string {
|
||||
return $this->name;
|
||||
},
|
||||
function (?string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function divergingTypeHintedGetAndSet(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
function (): int {
|
||||
return strlen($this->name);
|
||||
},
|
||||
function (?string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function typeHintedGet(): Attribute
|
||||
{
|
||||
return Attribute::get(function (): ?string {
|
||||
return $this->name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function typeHintedSet(): Attribute
|
||||
{
|
||||
return Attribute::set(function (?string $name) {
|
||||
$this->name = $name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function nonTypeHintedGetAndSet(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
function () {
|
||||
return $this->name;
|
||||
},
|
||||
function ($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function nonTypeHintedGet(): Attribute
|
||||
{
|
||||
return Attribute::get(function () {
|
||||
return $this->name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function nonTypeHintedSet(): Attribute
|
||||
{
|
||||
return Attribute::set(function ($name) {
|
||||
$this->name = $name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function parameterlessSet(): Attribute
|
||||
{
|
||||
return Attribute::set(function () {
|
||||
$this->name = null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ide-helper does not recognize this method being an Attribute
|
||||
* because the method has no actual return type;
|
||||
|
||||
@@ -11,7 +11,15 @@ use Illuminate\Database\Eloquent\Model;
|
||||
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models\Simple
|
||||
*
|
||||
* @property integer $id
|
||||
* @property int $diverging_type_hinted_get_and_set
|
||||
* @property string|null $name
|
||||
* @property-read mixed $non_type_hinted_get
|
||||
* @property mixed $non_type_hinted_get_and_set
|
||||
* @property-write mixed $non_type_hinted_set
|
||||
* @property-write mixed $parameterless_set
|
||||
* @property-read string|null $type_hinted_get
|
||||
* @property string|null $type_hinted_get_and_set
|
||||
* @property-write string|null $type_hinted_set
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
|
||||
@@ -20,6 +28,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
*/
|
||||
class Simple extends Model
|
||||
{
|
||||
// With a backed property
|
||||
protected function name(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
@@ -27,11 +36,84 @@ class Simple extends Model
|
||||
return $name;
|
||||
},
|
||||
function (?string $name): ?string {
|
||||
return $name === null ? null : ucfirst($name);
|
||||
return $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Without backed properties
|
||||
|
||||
protected function typeHintedGetAndSet(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
function (): ?string {
|
||||
return $this->name;
|
||||
},
|
||||
function (?string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function divergingTypeHintedGetAndSet(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
function (): int {
|
||||
return strlen($this->name);
|
||||
},
|
||||
function (?string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function typeHintedGet(): Attribute
|
||||
{
|
||||
return Attribute::get(function (): ?string {
|
||||
return $this->name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function typeHintedSet(): Attribute
|
||||
{
|
||||
return Attribute::set(function (?string $name) {
|
||||
$this->name = $name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function nonTypeHintedGetAndSet(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
function () {
|
||||
return $this->name;
|
||||
},
|
||||
function ($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function nonTypeHintedGet(): Attribute
|
||||
{
|
||||
return Attribute::get(function () {
|
||||
return $this->name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function nonTypeHintedSet(): Attribute
|
||||
{
|
||||
return Attribute::set(function ($name) {
|
||||
$this->name = $name;
|
||||
});
|
||||
}
|
||||
|
||||
protected function parameterlessSet(): Attribute
|
||||
{
|
||||
return Attribute::set(function () {
|
||||
$this->name = null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ide-helper does not recognize this method being an Attribute
|
||||
* because the method has no actual return type;
|
||||
|
||||
Reference in New Issue
Block a user