mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-18 01:57: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 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 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 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
|
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)
|
- Refactor resolving of null information for custom casted attribute types [#1330 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1330)
|
||||||
|
|
||||||
### Fixed
|
### 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)
|
- Catch exceptions when loading aliases [#1465 / dongm2ez](https://github.com/barryvdh/laravel-ide-helper/pull/1465)
|
||||||
|
|
||||||
### Added
|
### 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 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 `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
|
2023-02-04, 2.13.0
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
@@ -620,10 +620,7 @@ class ModelsCommand extends Command
|
|||||||
// methods that resemble mutators but aren't.
|
// methods that resemble mutators but aren't.
|
||||||
$reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) {
|
$reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) {
|
||||||
return !$methodReflection->isPrivate() && !(
|
return !$methodReflection->isPrivate() && !(
|
||||||
in_array(
|
$methodReflection->getDeclaringClass()->getName() === \Illuminate\Database\Eloquent\Model::class && (
|
||||||
\Illuminate\Database\Eloquent\Concerns\HasAttributes::class,
|
|
||||||
$methodReflection->getDeclaringClass()->getTraitNames()
|
|
||||||
) && (
|
|
||||||
$methodReflection->getName() === 'setClassCastableAttribute' ||
|
$methodReflection->getName() === 'setClassCastableAttribute' ||
|
||||||
$methodReflection->getName() === 'setEnumCastableAttribute'
|
$methodReflection->getName() === 'setEnumCastableAttribute'
|
||||||
)
|
)
|
||||||
@@ -649,18 +646,15 @@ class ModelsCommand extends Command
|
|||||||
$this->setProperty($name, $type, true, null, $comment);
|
$this->setProperty($name, $type, true, null, $comment);
|
||||||
}
|
}
|
||||||
} elseif ($isAttribute) {
|
} elseif ($isAttribute) {
|
||||||
$name = Str::snake($method);
|
$types = $this->getAttributeTypes($model, $reflection);
|
||||||
$types = $this->getAttributeReturnType($model, $reflection);
|
$type = $this->getTypeInModel($model, $types->get('get') ?: $types->get('set')) ?: null;
|
||||||
$comment = $this->getCommentFromDocBlock($reflection);
|
$this->setProperty(
|
||||||
|
Str::snake($method),
|
||||||
if ($types->has('get')) {
|
$type,
|
||||||
$type = $this->getTypeInModel($model, $types['get']);
|
$types->has('get'),
|
||||||
$this->setProperty($name, $type, true, null, $comment);
|
$types->has('set'),
|
||||||
}
|
$this->getCommentFromDocBlock($reflection)
|
||||||
|
);
|
||||||
if ($types->has('set')) {
|
|
||||||
$this->setProperty($name, null, null, true, $comment);
|
|
||||||
}
|
|
||||||
} elseif (
|
} elseif (
|
||||||
Str::startsWith($method, 'set') && Str::endsWith(
|
Str::startsWith($method, 'set') && Str::endsWith(
|
||||||
$method,
|
$method,
|
||||||
@@ -1192,7 +1186,10 @@ class ModelsCommand extends Command
|
|||||||
return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false);
|
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
|
// Private/protected ReflectionMethods require setAccessible prior to PHP 8.1
|
||||||
$reflectionMethod->setAccessible(true);
|
$reflectionMethod->setAccessible(true);
|
||||||
@@ -1200,13 +1197,25 @@ class ModelsCommand extends Command
|
|||||||
/** @var Attribute $attribute */
|
/** @var Attribute $attribute */
|
||||||
$attribute = $reflectionMethod->invoke($model);
|
$attribute = $reflectionMethod->invoke($model);
|
||||||
|
|
||||||
return collect([
|
$methods = new Collection();
|
||||||
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,
|
|
||||||
'set' => $attribute->set ? optional(new \ReflectionFunction($attribute->set))->getReturnType() : null,
|
if ($attribute->get) {
|
||||||
])
|
$methods['get'] = optional(new \ReflectionFunction($attribute->get))->getReturnType();
|
||||||
->filter()
|
}
|
||||||
|
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) {
|
->map(function ($type) {
|
||||||
if ($type instanceof \ReflectionUnionType) {
|
if ($type === null) {
|
||||||
|
$types = collect([]);
|
||||||
|
} elseif ($type instanceof \ReflectionUnionType) {
|
||||||
$types = collect($type->getTypes())
|
$types = collect($type->getTypes())
|
||||||
/** @var ReflectionType $reflectionType */
|
/** @var ReflectionType $reflectionType */
|
||||||
->map(function ($reflectionType) {
|
->map(function ($reflectionType) {
|
||||||
@@ -1217,7 +1226,7 @@ class ModelsCommand extends Command
|
|||||||
$types = collect($this->extractReflectionTypes($type));
|
$types = collect($this->extractReflectionTypes($type));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type->allowsNull()) {
|
if ($type && $type->allowsNull()) {
|
||||||
$types->push('null');
|
$types->push('null');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1467,8 +1476,7 @@ class ModelsCommand extends Command
|
|||||||
{
|
{
|
||||||
$reflection = $model instanceof ReflectionClass
|
$reflection = $model instanceof ReflectionClass
|
||||||
? $model
|
? $model
|
||||||
: new ReflectionObject($model)
|
: new ReflectionObject($model);
|
||||||
;
|
|
||||||
|
|
||||||
$className = trim($className, '\\');
|
$className = trim($className, '\\');
|
||||||
$writingToExternalFile = !$this->write || $this->write_mixin;
|
$writingToExternalFile = !$this->write || $this->write_mixin;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
|
|
||||||
class Simple extends Model
|
class Simple extends Model
|
||||||
{
|
{
|
||||||
|
// With a backed property
|
||||||
protected function name(): Attribute
|
protected function name(): Attribute
|
||||||
{
|
{
|
||||||
return new Attribute(
|
return new Attribute(
|
||||||
@@ -16,11 +17,84 @@ class Simple extends Model
|
|||||||
return $name;
|
return $name;
|
||||||
},
|
},
|
||||||
function (?string $name): ?string {
|
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
|
* ide-helper does not recognize this method being an Attribute
|
||||||
* because the method has no actual return type;
|
* 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
|
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models\Simple
|
||||||
*
|
*
|
||||||
* @property integer $id
|
* @property integer $id
|
||||||
|
* @property int $diverging_type_hinted_get_and_set
|
||||||
* @property string|null $name
|
* @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 newModelQuery()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
|
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
|
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
|
||||||
@@ -20,6 +28,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
*/
|
*/
|
||||||
class Simple extends Model
|
class Simple extends Model
|
||||||
{
|
{
|
||||||
|
// With a backed property
|
||||||
protected function name(): Attribute
|
protected function name(): Attribute
|
||||||
{
|
{
|
||||||
return new Attribute(
|
return new Attribute(
|
||||||
@@ -27,11 +36,84 @@ class Simple extends Model
|
|||||||
return $name;
|
return $name;
|
||||||
},
|
},
|
||||||
function (?string $name): ?string {
|
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
|
* ide-helper does not recognize this method being an Attribute
|
||||||
* because the method has no actual return type;
|
* because the method has no actual return type;
|
||||||
|
|||||||
Reference in New Issue
Block a user