diff --git a/readme.md b/readme.md index 0fee715..1ea4c37 100644 --- a/readme.md +++ b/readme.md @@ -40,6 +40,14 @@ You can choose to include helper files. This is not enabled by default, but you The Illuminate/Support/helpers.php is already set-up, but you can add/remove your own files in the config file. +### Work in progress: Model docs +If you don't want to write your properties yourself, you can use the (experimental) command `ide-helper:models` to generate +phpDocs, based on table columns, relations and getters/setters. Very alpha, so please provide feedback if you want. +Docs are written to a phpfile in the root of the project, so you can move the docs to the real model. + +For now, only models in app/models are scanned, with the same name as the file (eg. app/models/user.php -> User). +With the -M option, you can generate just one model. +`php artisan ide-helper:models -M Post` diff --git a/src/Barryvdh/LaravelIdeHelper/GeneratorCommand.php b/src/Barryvdh/LaravelIdeHelper/GeneratorCommand.php index f7f1732..c7a60b1 100644 --- a/src/Barryvdh/LaravelIdeHelper/GeneratorCommand.php +++ b/src/Barryvdh/LaravelIdeHelper/GeneratorCommand.php @@ -4,7 +4,11 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use DocBlock\Parser; use Illuminate\Foundation\AliasLoader; - +/** + * A command to generate autocomplete information for your IDE + * + * @author Barry vd. Heuvel + */ class GeneratorCommand extends Command { /** diff --git a/src/Barryvdh/LaravelIdeHelper/IdeHelperServiceProvider.php b/src/Barryvdh/LaravelIdeHelper/IdeHelperServiceProvider.php index d2a9fad..c1dc3a2 100644 --- a/src/Barryvdh/LaravelIdeHelper/IdeHelperServiceProvider.php +++ b/src/Barryvdh/LaravelIdeHelper/IdeHelperServiceProvider.php @@ -34,6 +34,12 @@ class IdeHelperServiceProvider extends ServiceProvider { return new GeneratorCommand; }); $this->commands('command.ide-helper.generate'); + + $this->app['command.ide-helper.models'] = $this->app->share(function($app) + { + return new ModelsCommand(); + }); + $this->commands('command.ide-helper.models'); } /** @@ -43,7 +49,7 @@ class IdeHelperServiceProvider extends ServiceProvider { */ public function provides() { - return array('command.ide-helper.generate'); + return array('command.ide-helper.generate', 'command.ide-helper.models'); } } diff --git a/src/Barryvdh/LaravelIdeHelper/ModelsCommand.php b/src/Barryvdh/LaravelIdeHelper/ModelsCommand.php new file mode 100644 index 0000000..157e28a --- /dev/null +++ b/src/Barryvdh/LaravelIdeHelper/ModelsCommand.php @@ -0,0 +1,219 @@ + + */ +class ModelsCommand extends Command { + + /** + * The console command name. + * + * @var string + */ + protected $name = 'ide-helper:models'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Generate autocompletion for models'; + + protected $properties = array(); + + + /** + * Execute the console command. + * + * @return void + */ + public function fire() + { + $filename = $this->argument('filename'); + $model = $this->option('model'); + + $content = $this->generateDocs($model); + + $written = \File::put($filename, $content); + + if($written !== false){ + $this->info("Model information was written to $filename"); + }else{ + $this->error("Failed to write model information to $filename"); + } + + } + + + /** + * Get the console command arguments. + * + * @return array + */ + protected function getArguments() + { + return array( + array('filename', InputArgument::OPTIONAL, 'The path to the helper file', 'model_phpdocs.php'), + ); + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return array( + array('model', "M", InputOption::VALUE_OPTIONAL, 'Which models to include', '*'), + ); + } + + protected function generateDocs($model){ + + + $output = " + */ +\n\n"; + + if($model === '*'){ + $models = $this->loadModels(); + }else{ + $models = array($model); + } + + foreach($models as $name){ + $this->properties = array(); + $model = new $name(); + $this->getPropertiesFromTable($model); + $this->getPropertiesFromMethods($model); + $output .= $this->createPhpDocs($name); + } + + return $output; + + } + + + protected function loadModels(){ + $models = array(); + foreach(\File::files(app_path().'/models') as $file){ + list($name, $ext) = explode('.', basename($file)); + $models[] = ucfirst($name); + } + return $models; + } + + protected function getPropertiesFromTable($model){ + $table = $model->getTable(); + $schema = $model->getConnection()->getDoctrineSchemaManager($table); + + $columns = $schema->listTableColumns($table); + + $properties = array(); + foreach ($columns as $column) { + $name = $column->getName(); + $type = $column->getType()->getName(); + switch($type){ + case 'string': + case 'text': + case 'date': + case 'time': + case 'guid': + $type = 'string'; + break; + case 'integer': + case 'bigint': + case 'smallint': + $type = 'integer'; + break; + case 'decimal': + case 'float': + $type = 'float'; + break; + case 'boolean': + $type = 'boolean'; + break; + case 'datetimetz': //String or DateTime, depending on $dates + case 'datetime': + $type = 'string|DateTime'; + break; + default: + $type = 'mixed'; + break; + } + $this->setProperty($name, $type, true, true); + } + } + + protected function getPropertiesFromMethods($model){ + foreach(get_class_methods($model) as $method){ + if(\Str::startsWith($method, 'get') && \Str::endsWith($method, 'Attribute') && $method !== 'setAttribute'){ + //Magic getAttribute + $name = \Str::snake(substr($method, 3, -9)); + if(!empty($name)){ + $this->setProperty($name, null, true, null); + } + }elseif(\Str::startsWith($method, 'set') && \Str::endsWith($method, 'Attribute') && $method !== 'setAttribute'){ + //Magic setAttribute + $name = \Str::snake(substr($method, 3, -9)); + if(!empty($name)){ + $this->setProperty($name, null, null, true); + } + }elseif(!method_exists('Eloquent', $method) && !\Str::startsWith($method, 'get')){ + //If not declared in parent class, assuming relation. + $this->setProperty($method, 'Eloquent', true, null); + } + } + } + + protected function setProperty($name, $type = null, $read = null, $write = null){ + if(!isset($this->properties[$name])){ + $this->properties[$name] = array(); + $this->properties[$name]['type'] = 'mixed'; + $this->properties[$name]['read'] = false; + $this->properties[$name]['write'] = false; + } + if($type !== null){ + $this->properties[$name]['type'] = $type; + } + if($read !== null){ + $this->properties[$name]['read'] = $read; + } + if($write !== null){ + $this->properties[$name]['write'] = $write; + } + } + + protected function createPhpDocs($class){ + $output = "/**\n *\n * Generated properties for $class\n *\n"; + foreach($this->properties as $name => $property){ + + if($property['read'] && $property['write']){ + $attr = 'property'; + }elseif($property['write']){ + $attr = 'property-write'; + }else{ + $attr = 'property-read'; + } + $type = $property['type']; + //TODO; check if returned as date + + $output .= " * @$attr $type \$$name \n"; + + } + $output .= " *\n */\nclass $class {}\n\n"; + return $output; + } + +}