You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I needed support for this in my app, but went about it in a slightly different way: by using PHP 8 Attributes to tell IDE Helper how to describe methods and properties. (...not to be confused with Laravel's Attributes!)
I haven't seen any other discussions about this approach. Assuming I've not missed anything, this feels like it could be a better or more flexible way to give developers more control over what IDE Helper produces.
The code below is specific to my implementation, but if any of the maintainers feel this could be expanded upon and included natively, I'll be happy to put a PR together. Or feel free to borrow the code for your own project 😄
1. The LaravelAttribute class
I created a custom PHP Attribute called LaravelAttribute, to annotate protected methods which set class properties that IDE Helper should recognise. It has sensible defaults set, but developers can customise how IDE Helper sets the property if they need to.
<?phpnamespaceApp\Models\Hooks;
useAttribute;
useBarryvdh\LaravelIdeHelper\Console\ModelsCommand;
useIlluminate\Support\Str;
/** This attribute specifies the expected return type for a Laravel Attribute accessor. */
#[Attribute(Attribute::TARGET_METHOD)]
class LaravelAttribute
{
publicfunction__construct(
publicstring|array|null$returnTypes,
publicbool$get = true,
publicbool$set = true,
publicbool$nullable = true,
public ?string$comment = null,
) {
}
/** Automatically apply the Attribute's properties to an IDE Helper docblock. */publicfunctionapply(ModelsCommand$command, string$methodName): void
{
$command->setProperty(
Str::of($methodName)->snake()->toString(),
collect($this->returnTypes)
->transform(function(string$type) {
$baseClass = Str::of($type)->before('<')->toString();
return (class_exists($baseClass) || interface_exists($baseClass))
&& (! Str::startsWith($baseClass, '\\')) ? ('\\' . $type) : $type;
})->join('|') ?: 'mixed',
$this->get,
$this->set,
$this->comment,
$this->nullable,
);
}
}
2. The ModelHooks class
IDE Helper supports hooks, so I created a new hook class. It collects all protected methods in each Model class, and checks if they have the LaravelAttribute attribute. If so, it passes the ModelsCommand instance to the apply() method of LaravelAttribute.
<?phpnamespaceApp\Models\Hooks;
useBarryvdh\LaravelIdeHelper\Console\ModelsCommand;
useIlluminate\Database\Eloquent\Model;
useReflectionAttribute;
useReflectionClass;
useReflectionMethod;
class ModelHooks implements \Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface
{
/** Use reflection to find LaravelAttributes on class methods, then apply properties with IDE Helper. */publicfunctionrun(ModelsCommand$command, Model$model): void
{
collect(
(newReflectionClass($model::class))->getMethods(ReflectionMethod::IS_PROTECTED)
)->mapWithKeys(fn(ReflectionMethod$method) => [
$method->getName() => collect($method->getAttributes(
LaravelAttribute::class,
ReflectionAttribute::IS_INSTANCEOF
))->transform(fn(ReflectionAttribute$attribute) => $attribute->newInstance())->first(),
])->filter()->each(
fn(LaravelAttribute$attribute, string$method) => $attribute->apply($command, $method)
);
}
}
Then registered it in the config/ide-helper.php file:
With the above in place, I can now add LaravelAttribute to any protected methods in my models. Here's an example for a User. You can specify multiple return types too, and it'll concatenate them (e.g. ['string', 'int'] is treated as a string|int union return type):
// app/Models/User.php:/** Get the User's full name. */
#[LaravelAttribute('string', set: false, nullable: false)]
protectedfunctionname(): Attribute
{
return Attribute::make(
get: fn($value, $attributes) => $attributes['first_name'] . '' . $attributes['last_name'],
);
}
/** Get or update an Address for the User. */
#[LaravelAttribute(Address::class)]
protectedfunctionaddress(): Attribute
{
return Attribute::make(
get: fn($value, $attributes) => newAddress($attributes['line_1'], $attributes['city']),
set: fn(Address$value) => [
'line_1' => $value->line_1,
'city' => $value->city,
],
);
}
/** Map the User's settings to a Collection. */
#[LaravelAttribute([Collection::class . '<string, bool>'], set: false)]
protectedfunctionsettings(): Attribute
{
return Attribute::make(
get: fn($value, $attributes) => collect([
'dark_mode' => $attributes['dark_mode'],
'timezone' => $attributes['timezone'],
'2fa_enabled' => $attributes['2fa_enabled'],
])
)->shouldCache();
}
When IDE Helper runs, it will automatically apply properties based on the LaravelAttribute annotations. None of these were previously recognised by IDE Helper, but have now been added to the docblock:
Set IDE Helper properties using PHP 8 Attributes
Hi! 👋 There are several recent issues and PRs about the lack of support for Laravel's
protected Attribute
return-type methods (see #1378 and #1339 for two examples).I needed support for this in my app, but went about it in a slightly different way: by using PHP 8 Attributes to tell IDE Helper how to describe methods and properties. (...not to be confused with Laravel's Attributes!)
I haven't seen any other discussions about this approach. Assuming I've not missed anything, this feels like it could be a better or more flexible way to give developers more control over what IDE Helper produces.
The code below is specific to my implementation, but if any of the maintainers feel this could be expanded upon and included natively, I'll be happy to put a PR together. Or feel free to borrow the code for your own project 😄
1. The
LaravelAttribute
classI created a custom PHP Attribute called
LaravelAttribute
, to annotate protected methods which set class properties that IDE Helper should recognise. It has sensible defaults set, but developers can customise how IDE Helper sets the property if they need to.2. The
ModelHooks
classIDE Helper supports hooks, so I created a new hook class. It collects all protected methods in each Model class, and checks if they have the
LaravelAttribute
attribute. If so, it passes theModelsCommand
instance to theapply()
method ofLaravelAttribute
.Then registered it in the
config/ide-helper.php
file:3. Applying
LaravelAttribute
to Model methodsWith the above in place, I can now add
LaravelAttribute
to any protected methods in my models. Here's an example for aUser
. You can specify multiple return types too, and it'll concatenate them (e.g.['string', 'int']
is treated as astring|int
union return type):When IDE Helper runs, it will automatically apply properties based on the
LaravelAttribute
annotations. None of these were previously recognised by IDE Helper, but have now been added to the docblock:The text was updated successfully, but these errors were encountered: