Skip to content

Commit

Permalink
Add comments like table columns. (#1168)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
biiiiiigmonster and mfn authored Mar 15, 2021
1 parent c5e18be commit c5c9b2b
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 45 additions & 8 deletions src/Console/ModelsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -538,13 +539,16 @@ protected function getPropertiesFromMethods($model)
//Magic set<name>Attribute
$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 set<name>Attribute
$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);
Expand All @@ -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()));
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -639,14 +644,16 @@ protected function getPropertiesFromMethods($model)
$method,
$collectionClassNameInModel . '|' . $relatedModel . '[]',
true,
null
null,
$comment
);
if ($this->write_model_relation_count_properties) {
$this->setProperty(
Str::snake($method) . '_count',
'int|null',
true,
false
// What kind of comments should be added to the relation count here?
);
}
} elseif ($relation === 'morphTo') {
Expand All @@ -655,7 +662,8 @@ protected function getPropertiesFromMethods($model)
$method,
$this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent',
true,
null
null,
$comment
);
} else {
//Single model is returned
Expand All @@ -664,7 +672,7 @@ protected function getPropertiesFromMethods($model)
$relatedModel,
true,
null,
'',
$comment,
$this->isRelationNullable($relation, $relationObj)
);
}
Expand Down Expand Up @@ -737,14 +745,15 @@ 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);

if (!isset($methods[strtolower($name)])) {
$this->methods[$name] = [];
$this->methods[$name]['type'] = $type;
$this->methods[$name]['arguments'] = $arguments;
$this->methods[$name]['comment'] = $comment;
}
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
*
Expand Down
136 changes: 136 additions & 0 deletions tests/Console/ModelsCommand/Comment/Models/Simple.php
Original file line number Diff line number Diff line change
@@ -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
{
}
}
24 changes: 24 additions & 0 deletions tests/Console/ModelsCommand/Comment/Test.php
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit c5c9b2b

Please sign in to comment.