Add comments like table columns. (#1168)

* add comment with @method & @property-read & property-write in models

* add readme comment

* add readme comment

* add comment tag unit-test

* add comment tag unit-test

* both a getter and a setter has a @comment test

* Update README.md

improve README

Co-authored-by: Markus Podar <[email protected]>

* Update CHANGELOG.md

Co-authored-by: Markus Podar <[email protected]>
This commit is contained in:
biiiiiigmonster
2021-03-15 07:52:11 +01:00
committed by GitHub
co-authored by Markus Podar
parent c5e18beff1
commit c5c9b2b951
6 changed files with 393 additions and 8 deletions
+1
View File
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
-------------- --------------
### Added ### Added
- Generate PHPDoc for Laravel 8.x factories [\#1074 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1074) - 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 ### Fixed
- Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124) - Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124)
+27
View File
@@ -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`. 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 #### 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 A new method to the eloquent models was added called `newEloquentBuilder` [Reference](https://timacdonald.me/dedicated-eloquent-model-query-builders/) where we can
+45 -8
View File
@@ -527,7 +527,8 @@ class ModelsCommand extends Command
$reflection = new \ReflectionMethod($model, $method); $reflection = new \ReflectionMethod($model, $method);
$type = $this->getReturnType($reflection); $type = $this->getReturnType($reflection);
$type = $this->getTypeInModel($model, $type); $type = $this->getTypeInModel($model, $type);
$this->setProperty($name, $type, true, null); $comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, $type, true, null, $comment);
} }
} elseif ( } elseif (
Str::startsWith($method, 'set') && Str::endsWith( Str::startsWith($method, 'set') && Str::endsWith(
@@ -538,13 +539,16 @@ class ModelsCommand extends Command
//Magic set<name>Attribute //Magic set<name>Attribute
$name = Str::snake(substr($method, 3, -9)); $name = Str::snake(substr($method, 3, -9));
if (!empty($name)) { 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') { } elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery') {
//Magic set<name>Attribute //Magic set<name>Attribute
$name = Str::camel(substr($method, 5)); $name = Str::camel(substr($method, 5));
if (!empty($name)) { if (!empty($name)) {
$reflection = new \ReflectionMethod($model, $method); $reflection = new \ReflectionMethod($model, $method);
$comment = $this->getCommentFromDocBlock($reflection);
$args = $this->getParameters($reflection); $args = $this->getParameters($reflection);
//Remove the first ($query) argument //Remove the first ($query) argument
array_shift($args); array_shift($args);
@@ -556,7 +560,7 @@ class ModelsCommand extends Command
$reflection->getDeclaringClass(), $reflection->getDeclaringClass(),
$reflection->getDeclaringClass()->getName() $reflection->getDeclaringClass()->getName()
); );
$this->setMethod($name, $builder . '|' . $modelName, $args); $this->setMethod($name, $builder . '|' . $modelName, $args, $comment);
} }
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) { } elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery())); $builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
@@ -608,6 +612,7 @@ class ModelsCommand extends Command
continue; continue;
} }
$comment = $this->getCommentFromDocBlock($reflection);
// Adding constraints requires reading model properties which // Adding constraints requires reading model properties which
// can cause errors. Since we don't need constraints we can // can cause errors. Since we don't need constraints we can
// disable them when we fetch the relation to avoid errors. // disable them when we fetch the relation to avoid errors.
@@ -639,7 +644,8 @@ class ModelsCommand extends Command
$method, $method,
$collectionClassNameInModel . '|' . $relatedModel . '[]', $collectionClassNameInModel . '|' . $relatedModel . '[]',
true, true,
null null,
$comment
); );
if ($this->write_model_relation_count_properties) { if ($this->write_model_relation_count_properties) {
$this->setProperty( $this->setProperty(
@@ -647,6 +653,7 @@ class ModelsCommand extends Command
'int|null', 'int|null',
true, true,
false false
// What kind of comments should be added to the relation count here?
); );
} }
} elseif ($relation === 'morphTo') { } elseif ($relation === 'morphTo') {
@@ -655,7 +662,8 @@ class ModelsCommand extends Command
$method, $method,
$this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent', $this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent',
true, true,
null null,
$comment
); );
} else { } else {
//Single model is returned //Single model is returned
@@ -664,7 +672,7 @@ class ModelsCommand extends Command
$relatedModel, $relatedModel,
true, true,
null, null,
'', $comment,
$this->isRelationNullable($relation, $relationObj) $this->isRelationNullable($relation, $relationObj)
); );
} }
@@ -737,7 +745,7 @@ class ModelsCommand extends Command
} }
} }
protected function setMethod($name, $type = '', $arguments = []) protected function setMethod($name, $type = '', $arguments = [], $comment = '')
{ {
$methods = array_change_key_case($this->methods, CASE_LOWER); $methods = array_change_key_case($this->methods, CASE_LOWER);
@@ -745,6 +753,7 @@ class ModelsCommand extends Command
$this->methods[$name] = []; $this->methods[$name] = [];
$this->methods[$name]['type'] = $type; $this->methods[$name]['type'] = $type;
$this->methods[$name]['arguments'] = $arguments; $this->methods[$name]['arguments'] = $arguments;
$this->methods[$name]['comment'] = $comment;
} }
} }
@@ -820,7 +829,11 @@ class ModelsCommand extends Command
continue; continue;
} }
$arguments = implode(', ', $method['arguments']); $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); $phpdoc->appendTag($tag);
} }
@@ -980,6 +993,30 @@ class ModelsCommand extends Command
return $this->getReturnTypeFromReflection($reflection); 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 * Get method return type based on it DocBlock comment
* *
@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Comment\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Simple extends Model
{
/**
* There is not comment.
*
* @return string
*/
public function getNotCommentAttribute(): string
{
}
/**
* comment There is not format comment, invalid.
*
* @return string
*/
public function getFakerCommentAttribute(): string
{
}
/**
* @comment There is format comment, success.
*
* @return string
*/
public function getFormatCommentAttribute(): string
{
}
/**
* @comment There is format comment, success.
* This is second line, success too.
*
* @return string
*/
public function getFormatCommentLineTwoAttribute(): string
{
}
/**
* @comment There is format comment, success.
* @comment This is others format comment, invalid.
*
* @return string
*/
public function getManyFormatCommentAttribute(): string
{
}
/**
* @comment Set the user's first name.
* @param $value
*/
public function setFirstNameAttribute($value)
{
}
/**
* @comment Scope a query to only include active users.
*
* @param $query
* @return mixed
*/
public function scopeActive($query)
{
return $query;
}
/**
* @comment HasMany relations.
*
* @return HasMany
*/
public function relationHasMany(): HasMany
{
return $this->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
{
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Comment;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->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();
}
}
@@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Comment\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Comment\Models\Simple
*
* @property integer $id
* @property string $both_same_name I'm a getter
* @property string $both_without_getter_comment
* @property-read string $faker_comment
* @property-read string $format_comment There is format comment, success.
* @property-read string $format_comment_line_two There is format comment, success.
* This is second line, success too.
* @property-read string $many_format_comment There is format comment, success.
* @property-read string $not_comment
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $relationHasMany HasMany relations.
* @property-read int|null $relation_has_many_count
* @property-read Simple|null $relationHasOne Others relations.
* @property-read Model|\Eloquent $relationMorphTo MorphTo relations.
* @property-write mixed $first_name Set the user's first name.
* @method static \Illuminate\Database\Eloquent\Builder|Simple active() Scope a query to only include active users.
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder|Simple whereId($value)
* @mixin \Eloquent
*/
class Simple extends Model
{
/**
* There is not comment.
*
* @return string
*/
public function getNotCommentAttribute(): string
{
}
/**
* comment There is not format comment, invalid.
*
* @return string
*/
public function getFakerCommentAttribute(): string
{
}
/**
* @comment There is format comment, success.
*
* @return string
*/
public function getFormatCommentAttribute(): string
{
}
/**
* @comment There is format comment, success.
* This is second line, success too.
*
* @return string
*/
public function getFormatCommentLineTwoAttribute(): string
{
}
/**
* @comment There is format comment, success.
* @comment This is others format comment, invalid.
*
* @return string
*/
public function getManyFormatCommentAttribute(): string
{
}
/**
* @comment Set the user's first name.
* @param $value
*/
public function setFirstNameAttribute($value)
{
}
/**
* @comment Scope a query to only include active users.
*
* @param $query
* @return mixed
*/
public function scopeActive($query)
{
return $query;
}
/**
* @comment HasMany relations.
*
* @return HasMany
*/
public function relationHasMany(): HasMany
{
return $this->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
{
}
}