From de35d6e73061b992b2b672f83b58b9bcaf331f03 Mon Sep 17 00:00:00 2001 From: Wimski Date: Thu, 28 May 2020 11:23:31 +0200 Subject: [PATCH 1/3] Model hooks --- CHANGELOG.md | 3 + README.md | 43 ++++++++++ config/ide-helper.php | 15 ++++ src/Console/ModelsCommand.php | 34 +++++++- src/Contracts/ModelHookInterface.php | 11 +++ .../ModelHooks/Hooks/CustomProperty.php | 15 ++++ .../ModelHooks/Models/Simple.php | 9 ++ .../Console/ModelsCommand/ModelHooks/Test.php | 84 +++++++++++++++++++ 8 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 src/Contracts/ModelHookInterface.php create mode 100644 tests/Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php create mode 100644 tests/Console/ModelsCommand/ModelHooks/Models/Simple.php create mode 100644 tests/Console/ModelsCommand/ModelHooks/Test.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b57894a..d85e76055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. [Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v2.9.2...master) -------------- +### Added +- Model hooks for adding custom information from external sources to model classes through the ModelsCommand [\#945 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/945) + ### Fixed - Running tests triggering post_migrate hooks [\#1193 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1193) - Array_merge error when config is cached prior to package install [\#1184 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1184) diff --git a/README.md b/README.md index bd2a927af..410aed3c1 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ Generation is done based on the files in your project, so they are always up-to- - [Usage](#usage) - [Automatic PHPDoc generation for Laravel Facades](#automatic-phpdoc-generation-for-laravel-facades) - [Automatic PHPDocs for models](#automatic-phpdocs-for-models) + - [Model Directories](#model-directories) + - [Ignore Models](#ignore-models) + - [Model Hooks](#model-hooks) - [Automatic PHPDocs generation for Laravel Fluent methods](#automatic-phpdocs-generation-for-laravel-fluent-methods) - [Auto-completion for factory builders](#auto-completion-for-factory-builders) - [PhpStorm Meta for Container instances](#phpstorm-meta-for-container-instances) @@ -175,6 +178,8 @@ With the `--write-mixin (-M)` option */ ``` +#### Model Directories + By default, models in `app/models` are scanned. The optional argument tells what models to use (also outside app/models). ```bash @@ -189,6 +194,8 @@ php artisan ide-helper:models --dir="path/to/models" --dir="app/src/Model" You can publish the config file (`php artisan vendor:publish`) and set the default directories. +#### Ignore Models + Models can be ignored using the `--ignore (-I)` option ```bash @@ -270,6 +277,42 @@ For those special cases, you can map them via the config `custom_db_types`. Exam ], ``` +#### Model Hooks + +If you need additional information on your model from sources that are not handled by default, you can hook in to the + generation process with model hooks to add extra information on the fly. + Simply create a class that implements `ModelHookInterface` and add it to the `model_hooks` array in the config: + + ```php +'model_hooks' => [ + MyCustomHook::class, +], +``` + +The `run` method will be called during generation for every model and receives the current running `ModelsCommand` and the current `Model`, e.g.: + +```php +class MyCustomHook implements ModelHookInterface +{ + public function run(ModelsCommand $command, Model $model): void + { + if (! $model instanceof MyModel) { + return; + } + + $command->setProperty('custom', 'string', true, false, 'My custom property'); + } +} +``` + +```php +/** + * MyModel + * + * @property integer $id + * @property-read string $custom +``` + ### Automatic PHPDocs generation for Laravel Fluent methods If you need PHPDocs support for Fluent methods in migration, for example diff --git a/config/ide-helper.php b/config/ide-helper.php index 4ea6c298a..3a52b3035 100644 --- a/config/ide-helper.php +++ b/config/ide-helper.php @@ -144,6 +144,21 @@ ], + /* + |-------------------------------------------------------------------------- + | Models hooks + |-------------------------------------------------------------------------- + | + | Define which hook classes you want to run for models to add custom information + | + | Hooks should implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface. + | + */ + + 'model_hooks' => array( + // App\Support\IdeHelper\MyModelHook::class + ), + /* |-------------------------------------------------------------------------- | Extra classes diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 4b7acae72..2b083c3b5 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -11,6 +11,7 @@ namespace Barryvdh\LaravelIdeHelper\Console; +use Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface; use Barryvdh\Reflection\DocBlock; use Barryvdh\Reflection\DocBlock\Context; use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer; @@ -279,6 +280,9 @@ protected function generateDocs($loadModels, $ignore = '') $this->getSoftDeleteMethods($model); $this->getCollectionMethods($model); $this->getFactoryMethods($model); + + $this->runModelHooks($model); + $output .= $this->createPhpDocs($name); $ignore[] = $name; $this->nullableColumns = []; @@ -336,7 +340,7 @@ protected function loadModels() * * @param \Illuminate\Database\Eloquent\Model $model */ - protected function castPropertiesType($model) + public function castPropertiesType($model) { $casts = $model->getCasts(); foreach ($casts as $name => $type) { @@ -412,7 +416,7 @@ protected function getTypeOverride($type) * * @param \Illuminate\Database\Eloquent\Model $model */ - protected function getPropertiesFromTable($model) + public function getPropertiesFromTable($model) { $table = $model->getConnection()->getTablePrefix() . $model->getTable(); $schema = $model->getConnection()->getDoctrineSchemaManager(); @@ -509,7 +513,7 @@ protected function getPropertiesFromTable($model) /** * @param \Illuminate\Database\Eloquent\Model $model */ - protected function getPropertiesFromMethods($model) + public function getPropertiesFromMethods($model) { $methods = get_class_methods($model); if ($methods) { @@ -721,7 +725,7 @@ protected function isRelationNullable(string $relation, Relation $relationObj): * @param string|null $comment * @param bool $nullable */ - protected function setProperty($name, $type = null, $read = null, $write = null, $comment = '', $nullable = false) + public function setProperty($name, $type = null, $read = null, $write = null, $comment = '', $nullable = false) { if (!isset($this->properties[$name])) { $this->properties[$name] = []; @@ -1351,4 +1355,26 @@ protected function getReflectionNamedType(ReflectionNamedType $paramType): strin return $parameterName; } + + /** + * @param \Illuminate\Database\Eloquent\Model $model + * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @throws \RuntimeException + */ + protected function runModelHooks($model): void + { + $hooks = $this->laravel['config']->get('ide-helper.model_hooks', array()); + + foreach ($hooks as $hook) { + $hookInstance = $this->laravel->make($hook); + + if (! $hookInstance instanceof ModelHookInterface) { + throw new \RuntimeException( + 'Your IDE helper model hook must implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface' + ); + } + + $hookInstance->run($this, $model); + } + } } diff --git a/src/Contracts/ModelHookInterface.php b/src/Contracts/ModelHookInterface.php new file mode 100644 index 000000000..52124704e --- /dev/null +++ b/src/Contracts/ModelHookInterface.php @@ -0,0 +1,11 @@ +setProperty('custom', 'string', true, false); + } +} diff --git a/tests/Console/ModelsCommand/ModelHooks/Models/Simple.php b/tests/Console/ModelsCommand/ModelHooks/Models/Simple.php new file mode 100644 index 000000000..5f09d8bff --- /dev/null +++ b/tests/Console/ModelsCommand/ModelHooks/Models/Simple.php @@ -0,0 +1,9 @@ +set('ide-helper', [ + 'model_locations' => [ + // This is calculated from the base_path() which points to + // vendor/orchestra/testbench-core/laravel + '/../../../../tests/Console/ModelsCommand/ModelHooks/Models', + ], + 'model_hooks' => [ + CustomProperty::class, + ], + ]); + } + + public function test(): void + { + $actualContent = null; + + $mockFilesystem = Mockery::mock(Filesystem::class); + $mockFilesystem + ->shouldReceive('get') + ->andReturn(file_get_contents(__DIR__ . '/Models/Simple.php')) + ->once(); + $mockFilesystem + ->shouldReceive('put') + ->with( + Mockery::any(), + Mockery::capture($actualContent) + ) + ->andReturn(1) // Simulate we wrote _something_ to the file + ->once(); + + $this->instance(Filesystem::class, $mockFilesystem); + + $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()); + + $expectedContent = <<<'PHP' +assertSame($expectedContent, $actualContent); + } +} From 4d4a9e2a70e9583449c0043a9f190ee42946dd92 Mon Sep 17 00:00:00 2001 From: laravel-ide-helper Date: Mon, 22 Feb 2021 08:33:49 +0000 Subject: [PATCH 2/3] composer fix-style --- config/ide-helper.php | 4 ++-- src/Console/ModelsCommand.php | 4 ++-- .../Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php | 2 ++ tests/Console/ModelsCommand/ModelHooks/Models/Simple.php | 2 ++ tests/Console/ModelsCommand/ModelHooks/Test.php | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/config/ide-helper.php b/config/ide-helper.php index 3a52b3035..4a6295405 100644 --- a/config/ide-helper.php +++ b/config/ide-helper.php @@ -155,9 +155,9 @@ | */ - 'model_hooks' => array( + 'model_hooks' => [ // App\Support\IdeHelper\MyModelHook::class - ), + ], /* |-------------------------------------------------------------------------- diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 2b083c3b5..4ee4b085b 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -1363,12 +1363,12 @@ protected function getReflectionNamedType(ReflectionNamedType $paramType): strin */ protected function runModelHooks($model): void { - $hooks = $this->laravel['config']->get('ide-helper.model_hooks', array()); + $hooks = $this->laravel['config']->get('ide-helper.model_hooks', []); foreach ($hooks as $hook) { $hookInstance = $this->laravel->make($hook); - if (! $hookInstance instanceof ModelHookInterface) { + if (!$hookInstance instanceof ModelHookInterface) { throw new \RuntimeException( 'Your IDE helper model hook must implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface' ); diff --git a/tests/Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php b/tests/Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php index 2a691380d..9f67461dd 100644 --- a/tests/Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php +++ b/tests/Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php @@ -1,5 +1,7 @@ Date: Fri, 26 Mar 2021 12:47:25 +0100 Subject: [PATCH 3/3] Fix test for ModelHooks --- tests/Console/ModelsCommand/ModelHooks/Test.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Console/ModelsCommand/ModelHooks/Test.php b/tests/Console/ModelsCommand/ModelHooks/Test.php index 7093ddcc0..057cfd2f6 100644 --- a/tests/Console/ModelsCommand/ModelHooks/Test.php +++ b/tests/Console/ModelsCommand/ModelHooks/Test.php @@ -60,6 +60,8 @@ public function test(): void $expectedContent = <<<'PHP'