Skip to content

Commit

Permalink
Model hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
wimski committed Feb 22, 2021
1 parent b21324b commit 7aa4a45
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 5 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)
- 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
- Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124)
Expand Down
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -243,6 +250,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
Expand Down
15 changes: 15 additions & 0 deletions config/ide-helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 31 additions & 5 deletions src/Console/ModelsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = [];
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -713,7 +717,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] = [];
Expand All @@ -737,7 +741,7 @@ protected function setProperty($name, $type = null, $read = null, $write = null,
}
}

protected function setMethod($name, $type = '', $arguments = [])
public function setMethod($name, $type = '', $arguments = [])
{
$methods = array_change_key_case($this->methods, CASE_LOWER);

Expand Down Expand Up @@ -1314,4 +1318,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);
}
}
}
11 changes: 11 additions & 0 deletions src/Contracts/ModelHookInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Barryvdh\LaravelIdeHelper\Contracts;

use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Illuminate\Database\Eloquent\Model;

interface ModelHookInterface
{
public function run(ModelsCommand $command, Model $model): void;
}
15 changes: 15 additions & 0 deletions tests/Console/ModelsCommand/ModelHooks/Hooks/CustomProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks;

use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface;
use Illuminate\Database\Eloquent\Model;

class CustomProperty implements ModelHookInterface
{
public function run(ModelsCommand $command, Model $model): void
{
$command->setProperty('custom', 'string', true, false);
}
}
9 changes: 9 additions & 0 deletions tests/Console/ModelsCommand/ModelHooks/Models/Simple.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Models;

use Illuminate\Database\Eloquent\Model;

class Simple extends Model
{
}
84 changes: 84 additions & 0 deletions tests/Console/ModelsCommand/ModelHooks/Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks;

use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\CustomProperty;
use Illuminate\Filesystem\Filesystem;
use Mockery;

class Test extends AbstractModelsCommand
{
protected function getEnvironmentSetUp($app)
{
parent::getEnvironmentSetUp($app);

$app['config']->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'
<?php
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Models\Simple
*
* @property int $id
* @property-read string $custom
* @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
{
}

PHP;

$this->assertSame($expectedContent, $actualContent);
}
}

0 comments on commit 7aa4a45

Please sign in to comment.