diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a0face4..b16a1cbe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. -------------- ### Added - Generate PHPDoc for Laravel 8.x factories [\#1074 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1074) +- Add a comment to a property like table columns [\#1168 / biiiiiigmonster](https://github.com/barryvdh/laravel-ide-helper/pull/1168) ### Fixed - Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124) diff --git a/README.md b/README.md index 01f6133fa..bd2a927af 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,33 @@ You may use the [`::withCount`](https://laravel.com/docs/master/eloquent-relatio By default, these attributes are generated in the phpdoc. You can turn them off by setting the config `write_model_relation_count_properties` to `false`. +#### Support `@comment` based on DocBlock + +In order to better support IDEs, relations and getters/setters can also add a comment to a property like table columns. Therefore a custom docblock `@comment` is used: +```php +class Users extends Model +{ + /** + * @comment Get User's full name + * + * @return string + */ + public function getFullNameAttribute(): string + { + return $this->first_name . ' ' .$this->last_name ; + } +} + +// => after generate models + +/** + * App\Models\Users + * + * @property-read string $full_name Get User's full name + * … + */ +``` + #### Dedicated Eloquent Builder methods A new method to the eloquent models was added called `newEloquentBuilder` [Reference](https://timacdonald.me/dedicated-eloquent-model-query-builders/) where we can diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 6fd0ab03a..4b7acae72 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -527,7 +527,8 @@ protected function getPropertiesFromMethods($model) $reflection = new \ReflectionMethod($model, $method); $type = $this->getReturnType($reflection); $type = $this->getTypeInModel($model, $type); - $this->setProperty($name, $type, true, null); + $comment = $this->getCommentFromDocBlock($reflection); + $this->setProperty($name, $type, true, null, $comment); } } elseif ( Str::startsWith($method, 'set') && Str::endsWith( @@ -538,13 +539,16 @@ protected function getPropertiesFromMethods($model) //Magic setAttribute $name = Str::snake(substr($method, 3, -9)); if (!empty($name)) { - $this->setProperty($name, null, null, true); + $reflection = new \ReflectionMethod($model, $method); + $comment = $this->getCommentFromDocBlock($reflection); + $this->setProperty($name, null, null, true, $comment); } } elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery') { //Magic setAttribute $name = Str::camel(substr($method, 5)); if (!empty($name)) { $reflection = new \ReflectionMethod($model, $method); + $comment = $this->getCommentFromDocBlock($reflection); $args = $this->getParameters($reflection); //Remove the first ($query) argument array_shift($args); @@ -556,7 +560,7 @@ protected function getPropertiesFromMethods($model) $reflection->getDeclaringClass(), $reflection->getDeclaringClass()->getName() ); - $this->setMethod($name, $builder . '|' . $modelName, $args); + $this->setMethod($name, $builder . '|' . $modelName, $args, $comment); } } elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) { $builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery())); @@ -608,6 +612,7 @@ protected function getPropertiesFromMethods($model) continue; } + $comment = $this->getCommentFromDocBlock($reflection); // Adding constraints requires reading model properties which // can cause errors. Since we don't need constraints we can // disable them when we fetch the relation to avoid errors. @@ -639,7 +644,8 @@ protected function getPropertiesFromMethods($model) $method, $collectionClassNameInModel . '|' . $relatedModel . '[]', true, - null + null, + $comment ); if ($this->write_model_relation_count_properties) { $this->setProperty( @@ -647,6 +653,7 @@ protected function getPropertiesFromMethods($model) 'int|null', true, false + // What kind of comments should be added to the relation count here? ); } } elseif ($relation === 'morphTo') { @@ -655,7 +662,8 @@ protected function getPropertiesFromMethods($model) $method, $this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent', true, - null + null, + $comment ); } else { //Single model is returned @@ -664,7 +672,7 @@ protected function getPropertiesFromMethods($model) $relatedModel, true, null, - '', + $comment, $this->isRelationNullable($relation, $relationObj) ); } @@ -737,7 +745,7 @@ protected function setProperty($name, $type = null, $read = null, $write = null, } } - protected function setMethod($name, $type = '', $arguments = []) + protected function setMethod($name, $type = '', $arguments = [], $comment = '') { $methods = array_change_key_case($this->methods, CASE_LOWER); @@ -745,6 +753,7 @@ protected function setMethod($name, $type = '', $arguments = []) $this->methods[$name] = []; $this->methods[$name]['type'] = $type; $this->methods[$name]['arguments'] = $arguments; + $this->methods[$name]['comment'] = $comment; } } @@ -820,7 +829,11 @@ protected function createPhpDocs($class) continue; } $arguments = implode(', ', $method['arguments']); - $tag = Tag::createInstance("@method static {$method['type']} {$name}({$arguments})", $phpdoc); + $tagLine = "@method static {$method['type']} {$name}({$arguments})"; + if ($method['comment'] !== '') { + $tagLine .= " {$method['comment']}"; + } + $tag = Tag::createInstance($tagLine, $phpdoc); $phpdoc->appendTag($tag); } @@ -980,6 +993,30 @@ protected function getReturnType(\ReflectionMethod $reflection): ?string return $this->getReturnTypeFromReflection($reflection); } + /** + * Get method comment based on it DocBlock comment + * + * @param \ReflectionMethod $reflection + * + * @return null|string + */ + protected function getCommentFromDocBlock(\ReflectionMethod $reflection) + { + $phpDocContext = (new ContextFactory())->createFromReflector($reflection); + $context = new Context( + $phpDocContext->getNamespace(), + $phpDocContext->getNamespaceAliases() + ); + $comment = ''; + $phpdoc = new DocBlock($reflection, $context); + + if ($phpdoc->hasTag('comment')) { + $comment = $phpdoc->getTagsByName('comment')[0]->getContent(); + } + + return $comment; + } + /** * Get method return type based on it DocBlock comment * diff --git a/tests/Console/ModelsCommand/Comment/Models/Simple.php b/tests/Console/ModelsCommand/Comment/Models/Simple.php new file mode 100644 index 000000000..dba5092c0 --- /dev/null +++ b/tests/Console/ModelsCommand/Comment/Models/Simple.php @@ -0,0 +1,136 @@ +hasMany(Simple::class); + } + + /** + * @comment MorphTo relations. + * @return MorphTo + */ + public function relationMorphTo(): MorphTo + { + return $this->morphTo(); + } + + /** + * @comment Others relations. + * @return HasOne + */ + public function relationHasOne(): HasOne + { + return $this->hasOne(Simple::class); + } + + /** + * @comment I'm a setter + */ + public function setBothSameNameAttribute(): void + { + } + + /** + * @comment I'm a getter + * @return string + */ + public function getBothSameNameAttribute(): string + { + } + + /** + * @comment I'm a setter + */ + public function setBothWithoutGetterCommentAttribute(): void + { + } + + /** + * @return string + */ + public function getBothWithoutGetterCommentAttribute(): string + { + } +} diff --git a/tests/Console/ModelsCommand/Comment/Test.php b/tests/Console/ModelsCommand/Comment/Test.php new file mode 100644 index 000000000..a98657b10 --- /dev/null +++ b/tests/Console/ModelsCommand/Comment/Test.php @@ -0,0 +1,24 @@ +app->make(ModelsCommand::class); + + $tester = $this->runCommand($command, [ + '--write' => true, + ]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay()); + $this->assertMatchesMockedSnapshot(); + } +} diff --git a/tests/Console/ModelsCommand/Comment/__snapshots__/Test__test__1.php b/tests/Console/ModelsCommand/Comment/__snapshots__/Test__test__1.php new file mode 100644 index 000000000..4f530e299 --- /dev/null +++ b/tests/Console/ModelsCommand/Comment/__snapshots__/Test__test__1.php @@ -0,0 +1,160 @@ +hasMany(Simple::class); + } + + /** + * @comment MorphTo relations. + * @return MorphTo + */ + public function relationMorphTo(): MorphTo + { + return $this->morphTo(); + } + + /** + * @comment Others relations. + * @return HasOne + */ + public function relationHasOne(): HasOne + { + return $this->hasOne(Simple::class); + } + + /** + * @comment I'm a setter + */ + public function setBothSameNameAttribute(): void + { + } + + /** + * @comment I'm a getter + * @return string + */ + public function getBothSameNameAttribute(): string + { + } + + /** + * @comment I'm a setter + */ + public function setBothWithoutGetterCommentAttribute(): void + { + } + + /** + * @return string + */ + public function getBothWithoutGetterCommentAttribute(): string + { + } +}