From 574c7d4ef377da61f7bdc2980937770a0bcd332e Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 8 Mar 2022 19:42:36 -0600 Subject: [PATCH 01/11] Initial work on re-aligning the scaffolding commands with Laravel --- modules/backend/console/CreateController.php | 42 +-- modules/backend/console/CreateFormWidget.php | 20 +- .../backend/console/CreateReportWidget.php | 20 +- modules/cms/console/CreateComponent.php | 19 +- modules/cms/console/CreateTheme.php | 11 +- .../console/scaffold/component/component.stub | 6 + modules/system/aliases.php | 2 + .../system/console/BaseScaffoldCommand.php | 338 ++++++++++++++++++ modules/system/console/CreateCommand.php | 33 +- modules/system/console/CreateMigration.php | 87 +++++ modules/system/console/CreateModel.php | 101 +++++- modules/system/console/CreatePlugin.php | 31 +- modules/system/console/CreateSettings.php | 21 +- .../console/scaffold/command/command.stub | 4 +- .../scaffold/migration/migration.create.stub | 31 ++ .../console/scaffold/migration/migration.stub | 28 ++ .../scaffold/migration/migration.update.stub | 32 ++ .../system/console/scaffold/model/model.stub | 2 +- .../console/scaffold/plugin/plugin.stub | 2 +- .../console/scaffold/plugin/version.stub | 2 +- 20 files changed, 734 insertions(+), 98 deletions(-) create mode 100644 modules/system/console/BaseScaffoldCommand.php create mode 100644 modules/system/console/CreateMigration.php create mode 100644 modules/system/console/scaffold/migration/migration.create.stub create mode 100644 modules/system/console/scaffold/migration/migration.stub create mode 100644 modules/system/console/scaffold/migration/migration.update.stub diff --git a/modules/backend/console/CreateController.php b/modules/backend/console/CreateController.php index d256e22ed1..c32cc4c929 100644 --- a/modules/backend/console/CreateController.php +++ b/modules/backend/console/CreateController.php @@ -1,21 +1,17 @@ (eg: Winter.Blog)} @@ -24,23 +20,22 @@ class CreateController extends GeneratorCommand {--model= : Defines the model name to use. If not provided, the singular name of the controller is used.}'; /** - * The console command description. - * - * @var string + * @var string The console command description. */ protected $description = 'Creates a new controller.'; /** - * The type of class being generated. - * - * @var string + * @var string The type of class being generated. */ protected $type = 'Controller'; /** - * A mapping of stub to generated file. - * - * @var array + * @var string The argument that the generated class name comes from + */ + protected $nameFrom = 'controller'; + + /** + * @var array A mapping of stub to generated file. */ protected $stubs = [ 'scaffold/controller/_list_toolbar.stub' => 'controllers/{{lower_name}}/_list_toolbar.htm', @@ -60,13 +55,10 @@ class CreateController extends GeneratorCommand */ protected function prepareVars() { - $pluginCode = $this->argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - - $controller = $this->argument('controller'); + $name = $this->getNameInput(); /* * Determine the model name to use, @@ -74,14 +66,14 @@ protected function prepareVars() */ $model = $this->option('model'); if (!$model) { - $model = Str::singular($controller); + $model = Str::singular($name); } return [ - 'name' => $controller, + 'name' => $name, 'model' => $model, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } } diff --git a/modules/backend/console/CreateFormWidget.php b/modules/backend/console/CreateFormWidget.php index 17cf2365ef..4beaa2c40c 100644 --- a/modules/backend/console/CreateFormWidget.php +++ b/modules/backend/console/CreateFormWidget.php @@ -1,8 +1,8 @@ argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - - $widget = $this->argument('widget'); + $name = $this->getNameInput(); return [ - 'name' => $widget, + 'name' => $name, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } } diff --git a/modules/backend/console/CreateReportWidget.php b/modules/backend/console/CreateReportWidget.php index be6d7f5e26..2ae4689dd1 100644 --- a/modules/backend/console/CreateReportWidget.php +++ b/modules/backend/console/CreateReportWidget.php @@ -1,8 +1,8 @@ argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - - $widget = $this->argument('widget'); + $name = $this->getNameInput(); return [ - 'name' => $widget, + 'name' => $name, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } } diff --git a/modules/cms/console/CreateComponent.php b/modules/cms/console/CreateComponent.php index d6eaa53d23..68dd236d76 100644 --- a/modules/cms/console/CreateComponent.php +++ b/modules/cms/console/CreateComponent.php @@ -1,8 +1,8 @@ argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - $component = $this->argument('component'); + $name = $this->getNameInput(); return [ - 'name' => $component, + 'name' => $name, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } } diff --git a/modules/cms/console/CreateTheme.php b/modules/cms/console/CreateTheme.php index fe917827b0..069538d48c 100644 --- a/modules/cms/console/CreateTheme.php +++ b/modules/cms/console/CreateTheme.php @@ -1,9 +1,9 @@ argument('theme')); + $code = str_slug($this->getNameInput()); return [ 'code' => $code, diff --git a/modules/cms/console/scaffold/component/component.stub b/modules/cms/console/scaffold/component/component.stub index 5f9bbbb7be..1e43cce065 100644 --- a/modules/cms/console/scaffold/component/component.stub +++ b/modules/cms/console/scaffold/component/component.stub @@ -4,6 +4,9 @@ use Cms\Classes\ComponentBase; class {{studly_name}} extends ComponentBase { + /** + * Gets the details for the component + */ public function componentDetails() { return [ @@ -12,6 +15,9 @@ class {{studly_name}} extends ComponentBase ]; } + /** + * Returns the properties provided by the component + */ public function defineProperties() { return []; diff --git a/modules/system/aliases.php b/modules/system/aliases.php index 85b36035b0..a35acf91a3 100644 --- a/modules/system/aliases.php +++ b/modules/system/aliases.php @@ -90,6 +90,8 @@ 'Illuminate\Support\Debug\HtmlDumper' => Symfony\Component\VarDumper\Dumper\HtmlDumper::class, // Scaffolds were moved from the Storm library into their corresponding modules. + 'Winter\Storm\Scaffold\GeneratorCommand' => System\Console\BaseScaffoldCommand::class, + 'October\Rain\Scaffold\GeneratorCommand' => System\Console\BaseScaffoldCommand::class, 'Winter\Storm\Scaffold\Console\CreateCommand' => System\Console\CreateCommand::class, 'Winter\Storm\Scaffold\Console\CreateModel' => System\Console\CreateModel::class, 'Winter\Storm\Scaffold\Console\CreatePlugin' => System\Console\CreatePlugin::class, diff --git a/modules/system/console/BaseScaffoldCommand.php b/modules/system/console/BaseScaffoldCommand.php new file mode 100644 index 0000000000..8e0e67d98f --- /dev/null +++ b/modules/system/console/BaseScaffoldCommand.php @@ -0,0 +1,338 @@ +files = new Filesystem; + } + + /** + * Execute the console command. + * + * @return bool|null + */ + public function handle() + { + // First we need to ensure that the given name is not a reserved word within the PHP + // language and that the class name will actually be valid. If it is not valid we + // can error now and prevent from polluting the filesystem using invalid files. + if ($this->isReservedName($this->getNameInput())) { + $this->error('The name "'.$this->getNameInput().'" is reserved by PHP.'); + + return false; + } + + $this->vars = $this->processVars($this->prepareVars()); + + $this->makeStubs(); + + $this->info($this->type . ' created successfully.'); + } + + /** + * Prepare variables for stubs. + * + * @return array + */ + abstract protected function prepareVars(); + + /** + * Make all stubs. + * + * @return void + */ + public function makeStubs() + { + $stubs = array_keys($this->stubs); + + // Make sure this command won't overwrite any existing files before running + if (!$this->option('force')) { + foreach ($stubs as $stub) { + $destinationFile = $this->getDestinationForStub($stub); + if ($this->files->exists($destinationFile)) { + throw new Exception("Cannot create the {$this->type}, $destinationFile already exists. Pass --force to overwrite existing files."); + } + } + } + + foreach ($stubs as $stub) { + $this->makeStub($stub); + } + } + + /** + * Get the destination path for the provided stub name + */ + protected function getDestinationForStub(string $stubName): string + { + return $this->getDestinationPath() . '/' . $this->stubs[$stubName]; + } + + /** + * Make a single stub. + * + * @param string $stubName The source filename for the stub. + */ + public function makeStub($stubName) + { + if (!isset($this->stubs[$stubName])) { + return; + } + + $sourceFile = $this->getSourcePath() . '/' . $stubName; + $destinationFile = $this->getDestinationForStub($stubName); + $destinationContent = $this->files->get($sourceFile); + + /* + * Parse each variable in to the destination content and path + */ + $destinationContent = Twig::parse($destinationContent, $this->vars); + $destinationFile = Twig::parse($destinationFile, $this->vars); + + $this->makeDirectory($destinationFile); + + $this->files->put($destinationFile, $destinationContent); + + $this->comment('File generated: ' . str_replace(base_path(), '', $destinationFile)); + } + + /** + * Build the directory for the class if necessary. + * + * @param string $path + * @return string + */ + protected function makeDirectory($path) + { + if (! $this->files->isDirectory(dirname($path))) { + $this->files->makeDirectory(dirname($path), 0777, true, true); + } + } + + /** + * Converts all variables to available modifier and case formats. + * Syntax is CASE_MODIFIER_KEY, eg: lower_plural_xxx + * + * @param array $vars The collection of original variables + * @return array A collection of variables with modifiers added + */ + protected function processVars($vars) + { + $cases = ['upper', 'lower', 'snake', 'studly', 'camel', 'title']; + $modifiers = ['plural', 'singular', 'title']; + + foreach ($vars as $key => $var) { + /* + * Apply cases, and cases with modifiers + */ + foreach ($cases as $case) { + $primaryKey = $case . '_' . $key; + $vars[$primaryKey] = $this->modifyString($case, $var); + + foreach ($modifiers as $modifier) { + $secondaryKey = $case . '_' . $modifier . '_' . $key; + $vars[$secondaryKey] = $this->modifyString([$modifier, $case], $var); + } + } + + /* + * Apply modifiers + */ + foreach ($modifiers as $modifier) { + $primaryKey = $modifier . '_' . $key; + $vars[$primaryKey] = $this->modifyString($modifier, $var); + } + } + + return $vars; + } + + /** + * Internal helper that handles modify a string, with extra logic. + * + * @param string|array $type + * @param string $string + * @return string + */ + protected function modifyString($type, $string) + { + if (is_array($type)) { + foreach ($type as $_type) { + $string = $this->modifyString($_type, $string); + } + + return $string; + } + + if ($type == 'title') { + $string = str_replace('_', ' ', Str::snake($string)); + } + + return Str::$type($string); + } + + /** + * Get the plugin path from the input. + * + * @return string + */ + protected function getDestinationPath() + { + $plugin = $this->getPluginIdentifier(); + + $parts = explode('.', $plugin); + $name = array_pop($parts); + $author = array_pop($parts); + + return plugins_path(strtolower($author) . '/' . strtolower($name)); + } + + /** + * Get the source file path. + * + * @return string + */ + protected function getSourcePath() + { + $className = get_class($this); + $class = new ReflectionClass($className); + + return dirname($class->getFileName()); + } + + /** + * Get the desired class name from the input. + */ + protected function getNameInput(): string + { + return trim($this->argument($this->nameFrom)); + } + + /** + * Checks whether the given name is reserved. + */ + protected function isReservedName(string $name): bool + { + $name = strtolower($name); + + return in_array($name, $this->reservedNames); + } +} diff --git a/modules/system/console/CreateCommand.php b/modules/system/console/CreateCommand.php index 3a5f546bca..740900b1ae 100644 --- a/modules/system/console/CreateCommand.php +++ b/modules/system/console/CreateCommand.php @@ -1,8 +1,9 @@ (eg: Winter.Blog)} - {name : The name of the command to generate. (eg: create)} + {name : The name of the command to generate. (eg: ImportPosts)} + {--command= : The terminal command that should be assigned. (eg: blog:importposts)} {--f|force : Overwrite existing files with generated files.}'; /** @@ -22,6 +24,13 @@ class CreateCommand extends GeneratorCommand */ protected $description = 'Creates a new console command.'; + /** + * @var array List of commands that this command replaces (aliases) + */ + protected $replaces = [ + 'make:command', + ]; + /** * @var string The type of class being generated. */ @@ -41,17 +50,23 @@ class CreateCommand extends GeneratorCommand */ protected function prepareVars() { - $pluginCode = $this->argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - $command = $this->argument('name'); + $name = $this->getNameInput(); + $command = trim($this->option('command') ?? strtolower("{$plugin}:{$name}")); + + // More strict than the base Symfony validateName() + // method, make a PR if it's a problem for you + if (preg_match('/^[a-z]++(:[a-z]++)*$/', $command) !== 1) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $command)); + } return [ - 'name' => $command, + 'name' => $name, + 'command' => $command, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } } diff --git a/modules/system/console/CreateMigration.php b/modules/system/console/CreateMigration.php new file mode 100644 index 0000000000..811df264f3 --- /dev/null +++ b/modules/system/console/CreateMigration.php @@ -0,0 +1,87 @@ +(eg: Winter.Blog)} + {name : The name of the migration to generate. (eg: CreatePostTable)} + {--f|force : Overwrite existing files with generated files.} + {--m|model : The model to create a migration for. (eg: Post)} + {--table= : The table to migrate, defaults to autogenerated from the provided model. (eg: winter_blog_posts)} + {--c|create : Generate a migration that creates the specified table} + {--u|update : Generate a migration that updates the specified table} + '; + + /** + * @var string The console command description. + */ + protected $description = 'Creates a new migration.'; + + /** + * @var array List of commands that this command replaces (aliases) + */ + protected $replaces = [ + 'make:migration', + ]; + + /** + * @var string The type of class being generated. + */ + protected $type = 'Migration'; + + /** + * @var array A mapping of stubs to generated files. + */ + protected $stubs = [ + 'scaffold/migration/create_table.stub' => 'updates/create_{{snake_plural_name}}_table.php', + ]; + + /** + * Prepare variables for stubs. + * + * @return array + */ + protected function prepareVars() + { + $parts = explode('.', $this->getPluginIdentifier()); + $plugin = array_pop($parts); + $author = array_pop($parts); + $name = $this->getNameInput(); + $table = $this->option('table'); + $model = $this->option('model'); + + if (empty($table) && !empty($model)) { + $modelClass = "\{$author}\{$plugin}\Models\{$model}"; + if (class_exists($modelClass)) { + $table = (new $modelClass)->getTable(); + } + } + + dd($author, $plugin, $name, $table, $model); + + return [ + 'name' => $name, + 'author' => $author, + 'plugin' => $plugin, + ]; + } +} diff --git a/modules/system/console/CreateModel.php b/modules/system/console/CreateModel.php index bf425ca09c..34d4e10b5a 100644 --- a/modules/system/console/CreateModel.php +++ b/modules/system/console/CreateModel.php @@ -1,8 +1,9 @@ (eg: Winter.Blog)} {model : The name of the model to generate. (eg: Post)} - {--f|force : Overwrite existing files with generated files.}'; + {--f|force : Overwrite existing files with generated files.} + + {--a|all : Generate a controller, migration, & seeder for the model} + {--c|controller : Create a new controller for the model} + {--s|seed : Create a new seeder for the model} + {--p|pivot : Indicates if the generated model should be a custom intermediate table model} + {--no-migration : Don\'t create a migration file for the model} + '; /** * @var string The console command description. */ protected $description = 'Creates a new model.'; + /** + * @var array List of commands that this command replaces (aliases) + */ + protected $replaces = [ + 'make:model', + ]; + /** * @var string The type of class being generated. */ protected $type = 'Model'; + /** + * @var string The argument that the generated class name comes from + */ + protected $nameFrom = 'model'; + /** * @var array A mapping of stubs to generated files. */ @@ -34,9 +54,37 @@ class CreateModel extends GeneratorCommand 'scaffold/model/model.stub' => 'models/{{studly_name}}.php', 'scaffold/model/fields.stub' => 'models/{{lower_name}}/fields.yaml', 'scaffold/model/columns.stub' => 'models/{{lower_name}}/columns.yaml', - 'scaffold/model/create_table.stub' => 'updates/create_{{snake_plural_name}}_table.php', ]; + /** + * Execute the console command. + * + * @return void + */ + public function handle() + { + if (parent::handle() === false && !$this->option('force')) { + return false; + } + + if ($this->option('all')) { + $this->input->setOption('controller', true); + $this->input->setOption('seed', true); + } + + if ($this->option('controller')) { + $this->createController(); + } + + if ($this->option('seed')) { + $this->createSeeder(); + } + + if ($this->option('no-migration') !== false) { + $this->createMigration(); + } + } + /** * Prepare variables for stubs. * @@ -44,18 +92,49 @@ class CreateModel extends GeneratorCommand */ protected function prepareVars() { - $pluginCode = $this->argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - - $model = $this->argument('model'); + $name = $this->getNameInput(); return [ - 'name' => $model, + 'name' => $name, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } + + /** + * Create a migration for the model. + */ + public function createMigration() + { + $this->call('create:migration', [ + 'plugin' => $this->getPluginIdentifier(), + '--model' => $this->getNameInput(), + ]); + } + + /** + * Create a seeder for the model. + */ + public function createSeeder() + { + $this->call('create:seeder', [ + 'plugin' => $this->getPluginIdentifier(), + 'model' => $this->getNameInput(), + ]); + } + + /** + * Create a controller for the model. + */ + public function createController() + { + $this->call('create:controller', [ + 'plugin' => $this->getPluginIdentifier(), + 'controller' => Str::pluralize($this->argument('model')), + '--model' => $this->getNameInput(), + ]); + } } diff --git a/modules/system/console/CreatePlugin.php b/modules/system/console/CreatePlugin.php index 85268ea7d4..b4ecd018e4 100644 --- a/modules/system/console/CreatePlugin.php +++ b/modules/system/console/CreatePlugin.php @@ -1,8 +1,8 @@ argument('plugin'); - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getNameInput()); - if (count($parts) != 2) { + if (count($parts) !== 2) { $this->error('Invalid plugin name, either too many dots or not enough.'); $this->error('Example name: AuthorName.PluginName'); return; } - - $pluginName = array_pop($parts); - $authorName = array_pop($parts); + $plugin = array_pop($parts); + $author = array_pop($parts); return [ - 'name' => $pluginName, - 'author' => $authorName, + 'name' => $plugin, + 'author' => $author, ]; } + + /** + * Don't suggest any values, this command is for creating plugins + */ + public function suggestPluginValues(): array + { + return []; + } } diff --git a/modules/system/console/CreateSettings.php b/modules/system/console/CreateSettings.php index 4c2b26b887..c61ced7d97 100644 --- a/modules/system/console/CreateSettings.php +++ b/modules/system/console/CreateSettings.php @@ -1,8 +1,8 @@ (eg: Winter.Blog)} - {settings : The name of the settings model to generate. (eg: BlogSettings)} + {settings? : The name of the settings model to generate. (eg: BlogSettings)} {--f|force : Overwrite existing files with generated files.}'; /** @@ -27,6 +27,11 @@ class CreateSettings extends GeneratorCommand */ protected $type = 'Settings Model'; + /** + * @var string The argument that the generated class name comes from + */ + protected $nameFrom = 'settings'; + /** * @var array A mapping of stubs to generated files. */ @@ -42,17 +47,15 @@ class CreateSettings extends GeneratorCommand */ protected function prepareVars() { - $pluginCode = $this->argument('plugin'); - - $parts = explode('.', $pluginCode); + $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); $author = array_pop($parts); - $settings = $this->argument('settings') ?? 'Settings'; + $name = $this->getNameInput() ?? 'Settings'; return [ - 'name' => $settings, + 'name' => $name, 'author' => $author, - 'plugin' => $plugin + 'plugin' => $plugin, ]; } } diff --git a/modules/system/console/scaffold/command/command.stub b/modules/system/console/scaffold/command/command.stub index 67ccbb43ed..24ef8b2f3e 100644 --- a/modules/system/console/scaffold/command/command.stub +++ b/modules/system/console/scaffold/command/command.stub @@ -7,12 +7,12 @@ class {{studly_name}} extends Command /** * @var string The console command name. */ - protected static $defaultName = '{{lower_plugin}}:{{lower_name}}'; + protected static $defaultName = '{{ lower_command }}'; /** * @var string The name and signature of this command. */ - protected $signature = '{{lower_plugin}}:{{lower_name}} + protected $signature = '{{ lower_command }} {myCustomArgument : Example argument. Additional information} {--f|force : Force the operation to run and ignore production warnings and confirmation questions.}'; diff --git a/modules/system/console/scaffold/migration/migration.create.stub b/modules/system/console/scaffold/migration/migration.create.stub new file mode 100644 index 0000000000..f193cd696f --- /dev/null +++ b/modules/system/console/scaffold/migration/migration.create.stub @@ -0,0 +1,31 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('{{ table }}'); + } +}; diff --git a/modules/system/console/scaffold/migration/migration.stub b/modules/system/console/scaffold/migration/migration.stub new file mode 100644 index 0000000000..eb54698869 --- /dev/null +++ b/modules/system/console/scaffold/migration/migration.stub @@ -0,0 +1,28 @@ + '{{name}}', 'description' => 'No description provided yet...', 'author' => '{{author}}', - 'icon' => 'icon-leaf' + 'icon' => 'icon-leaf', ]; } diff --git a/modules/system/console/scaffold/plugin/version.stub b/modules/system/console/scaffold/plugin/version.stub index 34b2bc0f97..326445e362 100644 --- a/modules/system/console/scaffold/plugin/version.stub +++ b/modules/system/console/scaffold/plugin/version.stub @@ -1 +1 @@ -1.0.0: First version of {{name}} +"1.0.0": "First version of {{name}}" From c37190065abf0ee49a4cfd9a5f005375fa20e53e Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Mar 2022 14:30:59 -0600 Subject: [PATCH 02/11] Simplify diff --- modules/system/aliases.php | 2 - .../system/console/BaseScaffoldCommand.php | 320 +----------------- 2 files changed, 3 insertions(+), 319 deletions(-) diff --git a/modules/system/aliases.php b/modules/system/aliases.php index a35acf91a3..85b36035b0 100644 --- a/modules/system/aliases.php +++ b/modules/system/aliases.php @@ -90,8 +90,6 @@ 'Illuminate\Support\Debug\HtmlDumper' => Symfony\Component\VarDumper\Dumper\HtmlDumper::class, // Scaffolds were moved from the Storm library into their corresponding modules. - 'Winter\Storm\Scaffold\GeneratorCommand' => System\Console\BaseScaffoldCommand::class, - 'October\Rain\Scaffold\GeneratorCommand' => System\Console\BaseScaffoldCommand::class, 'Winter\Storm\Scaffold\Console\CreateCommand' => System\Console\CreateCommand::class, 'Winter\Storm\Scaffold\Console\CreateModel' => System\Console\CreateModel::class, 'Winter\Storm\Scaffold\Console\CreatePlugin' => System\Console\CreatePlugin::class, diff --git a/modules/system/console/BaseScaffoldCommand.php b/modules/system/console/BaseScaffoldCommand.php index 8e0e67d98f..f373e42b89 100644 --- a/modules/system/console/BaseScaffoldCommand.php +++ b/modules/system/console/BaseScaffoldCommand.php @@ -1,300 +1,17 @@ files = new Filesystem; - } - - /** - * Execute the console command. - * - * @return bool|null - */ - public function handle() - { - // First we need to ensure that the given name is not a reserved word within the PHP - // language and that the class name will actually be valid. If it is not valid we - // can error now and prevent from polluting the filesystem using invalid files. - if ($this->isReservedName($this->getNameInput())) { - $this->error('The name "'.$this->getNameInput().'" is reserved by PHP.'); - - return false; - } - - $this->vars = $this->processVars($this->prepareVars()); - - $this->makeStubs(); - - $this->info($this->type . ' created successfully.'); - } - - /** - * Prepare variables for stubs. - * - * @return array - */ - abstract protected function prepareVars(); - - /** - * Make all stubs. - * - * @return void - */ - public function makeStubs() - { - $stubs = array_keys($this->stubs); - - // Make sure this command won't overwrite any existing files before running - if (!$this->option('force')) { - foreach ($stubs as $stub) { - $destinationFile = $this->getDestinationForStub($stub); - if ($this->files->exists($destinationFile)) { - throw new Exception("Cannot create the {$this->type}, $destinationFile already exists. Pass --force to overwrite existing files."); - } - } - } - - foreach ($stubs as $stub) { - $this->makeStub($stub); - } - } - - /** - * Get the destination path for the provided stub name - */ - protected function getDestinationForStub(string $stubName): string - { - return $this->getDestinationPath() . '/' . $this->stubs[$stubName]; - } - - /** - * Make a single stub. - * - * @param string $stubName The source filename for the stub. - */ - public function makeStub($stubName) - { - if (!isset($this->stubs[$stubName])) { - return; - } - - $sourceFile = $this->getSourcePath() . '/' . $stubName; - $destinationFile = $this->getDestinationForStub($stubName); - $destinationContent = $this->files->get($sourceFile); - - /* - * Parse each variable in to the destination content and path - */ - $destinationContent = Twig::parse($destinationContent, $this->vars); - $destinationFile = Twig::parse($destinationFile, $this->vars); - - $this->makeDirectory($destinationFile); - - $this->files->put($destinationFile, $destinationContent); - - $this->comment('File generated: ' . str_replace(base_path(), '', $destinationFile)); - } - - /** - * Build the directory for the class if necessary. - * - * @param string $path - * @return string - */ - protected function makeDirectory($path) - { - if (! $this->files->isDirectory(dirname($path))) { - $this->files->makeDirectory(dirname($path), 0777, true, true); - } - } - - /** - * Converts all variables to available modifier and case formats. - * Syntax is CASE_MODIFIER_KEY, eg: lower_plural_xxx - * - * @param array $vars The collection of original variables - * @return array A collection of variables with modifiers added - */ - protected function processVars($vars) - { - $cases = ['upper', 'lower', 'snake', 'studly', 'camel', 'title']; - $modifiers = ['plural', 'singular', 'title']; - - foreach ($vars as $key => $var) { - /* - * Apply cases, and cases with modifiers - */ - foreach ($cases as $case) { - $primaryKey = $case . '_' . $key; - $vars[$primaryKey] = $this->modifyString($case, $var); - - foreach ($modifiers as $modifier) { - $secondaryKey = $case . '_' . $modifier . '_' . $key; - $vars[$secondaryKey] = $this->modifyString([$modifier, $case], $var); - } - } - - /* - * Apply modifiers - */ - foreach ($modifiers as $modifier) { - $primaryKey = $modifier . '_' . $key; - $vars[$primaryKey] = $this->modifyString($modifier, $var); - } - } - - return $vars; - } - - /** - * Internal helper that handles modify a string, with extra logic. - * - * @param string|array $type - * @param string $string - * @return string - */ - protected function modifyString($type, $string) - { - if (is_array($type)) { - foreach ($type as $_type) { - $string = $this->modifyString($_type, $string); - } - - return $string; - } - - if ($type == 'title') { - $string = str_replace('_', ' ', Str::snake($string)); - } - - return Str::$type($string); - } - /** * Get the plugin path from the input. * * @return string */ - protected function getDestinationPath() + protected function getDestinationPath(): string { $plugin = $this->getPluginIdentifier(); @@ -304,35 +21,4 @@ protected function getDestinationPath() return plugins_path(strtolower($author) . '/' . strtolower($name)); } - - /** - * Get the source file path. - * - * @return string - */ - protected function getSourcePath() - { - $className = get_class($this); - $class = new ReflectionClass($className); - - return dirname($class->getFileName()); - } - - /** - * Get the desired class name from the input. - */ - protected function getNameInput(): string - { - return trim($this->argument($this->nameFrom)); - } - - /** - * Checks whether the given name is reserved. - */ - protected function isReservedName(string $name): bool - { - $name = strtolower($name); - - return in_array($name, $this->reservedNames); - } } From 0641ab7243492b3637948a546fb19fce8a7874c5 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Mar 2022 15:07:00 -0600 Subject: [PATCH 03/11] Adjusted interfaces and made create:plugin include translations --- modules/backend/console/CreateController.php | 4 +- modules/backend/console/CreateFormWidget.php | 4 +- .../backend/console/CreateReportWidget.php | 4 +- modules/cms/console/CreateComponent.php | 4 +- modules/cms/console/CreateTheme.php | 8 +- .../system/console/BaseScaffoldCommand.php | 93 ++++++++++++++++++- modules/system/console/CreateCommand.php | 4 +- modules/system/console/CreateMigration.php | 4 +- modules/system/console/CreateModel.php | 4 +- modules/system/console/CreatePlugin.php | 41 ++++---- modules/system/console/CreateSettings.php | 4 +- .../console/scaffold/plugin/plugin.stub | 58 +++++------- .../console/scaffold/plugin/version.stub | 3 +- .../console/traits/HasPluginArgument.php | 10 +- 14 files changed, 152 insertions(+), 93 deletions(-) diff --git a/modules/backend/console/CreateController.php b/modules/backend/console/CreateController.php index c32cc4c929..b0424d3100 100644 --- a/modules/backend/console/CreateController.php +++ b/modules/backend/console/CreateController.php @@ -50,10 +50,8 @@ class CreateController extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * return @array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/backend/console/CreateFormWidget.php b/modules/backend/console/CreateFormWidget.php index 4beaa2c40c..86db87a67c 100644 --- a/modules/backend/console/CreateFormWidget.php +++ b/modules/backend/console/CreateFormWidget.php @@ -54,10 +54,8 @@ class CreateFormWidget extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * return @array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/backend/console/CreateReportWidget.php b/modules/backend/console/CreateReportWidget.php index 2ae4689dd1..a3c22885a9 100644 --- a/modules/backend/console/CreateReportWidget.php +++ b/modules/backend/console/CreateReportWidget.php @@ -52,10 +52,8 @@ class CreateReportWidget extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * return @array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/cms/console/CreateComponent.php b/modules/cms/console/CreateComponent.php index 68dd236d76..8e829de9b4 100644 --- a/modules/cms/console/CreateComponent.php +++ b/modules/cms/console/CreateComponent.php @@ -52,10 +52,8 @@ class CreateComponent extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * return @array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/cms/console/CreateTheme.php b/modules/cms/console/CreateTheme.php index 069538d48c..9f8e702d6c 100644 --- a/modules/cms/console/CreateTheme.php +++ b/modules/cms/console/CreateTheme.php @@ -62,10 +62,8 @@ class CreateTheme extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * return @array */ - protected function prepareVars() + protected function prepareVars(): array { /* * Extract the author and name from the plugin code @@ -79,10 +77,8 @@ protected function prepareVars() /** * Get the plugin path from the input. - * - * @return string */ - protected function getDestinationPath() + protected function getDestinationPath(): string { $code = $this->prepareVars()['code']; diff --git a/modules/system/console/BaseScaffoldCommand.php b/modules/system/console/BaseScaffoldCommand.php index f373e42b89..ba2c0cf0be 100644 --- a/modules/system/console/BaseScaffoldCommand.php +++ b/modules/system/console/BaseScaffoldCommand.php @@ -1,5 +1,7 @@ getPluginIdentifier(); + $parts = explode('.', $pluginCode); + + if (count($parts) !== 2) { + throw new InvalidArgumentException("Invalid plugin name, either too many dots or not enough. Example: Author.PluginName"); + } + + $pluginName = array_pop($parts); + $authorName = array_pop($parts); + + return [ + 'name' => $this->getNameInput(), + 'plugin' => $pluginName, + 'author' => $authorName, + ]; + } + + /** + * Converts all variables to available modifier and case formats and adds plugin helpers + */ + protected function processVars(array $vars): array + { + $vars = parent::processVars($vars); + + $vars['plugin_id'] = "{$vars['lower_author']}.{$vars['lower_plugin']}"; + $vars['plugin_code'] = "{$vars['studly_author']}.{$vars['studly_plugin']}"; + $vars['plugin_url'] = "{$vars['lower_author']}/{$vars['lower_plugin']}"; + $vars['plugin_folder'] = "{$vars['lower_author']}" . DIRECTORY_SEPARATOR . "{$vars['lower_plugin']}"; + $vars['plugin_namespace'] = "{$vars['studly_author']}\\{$vars['studly_plugin']}"; + + return $vars; + } + + /** + * Get the base path to output generated stubs to */ protected function getDestinationPath(): string { @@ -21,4 +62,50 @@ protected function getDestinationPath(): string return plugins_path(strtolower($author) . '/' . strtolower($name)); } + + /** + * Make all stubs. + */ + public function makeStubs(): void + { + parent::makeStubs(); + + // Get the language keys to be set + $langKeys = $this->getLangKeys(); + if (empty($langKeys)) { + return; + } + + // Generate the path to the localization file to modify + $langFilePath = plugins_path( + $this->vars['plugin_folder'] + . DIRECTORY_SEPARATOR + . 'lang' + . DIRECTORY_SEPARATOR + . $this->laravel->getLocale() + . DIRECTORY_SEPARATOR + . 'lang.php' + ); + if (!file_exists($langFilePath)) { + $this->makeDirectory($langFilePath); + $comment = 'File generated: ' . str_replace(base_path(), '', $langFilePath); + } else { + $comment = 'File updated: ' . str_replace(base_path(), '', $langFilePath); + } + + // Store the localization messages to the determined file path + ArrayFile::open($langFilePath)->set($langKeys)->write(); + + // Inform the user + $this->comment($comment); + } + + /** + * Gets the localization keys and values to be stored in the plugin's localization files + * Can reference $this->vars and $this->laravel->getLocale() internally + */ + protected function getLangKeys(): array + { + return []; + } } diff --git a/modules/system/console/CreateCommand.php b/modules/system/console/CreateCommand.php index 740900b1ae..9bda65bc15 100644 --- a/modules/system/console/CreateCommand.php +++ b/modules/system/console/CreateCommand.php @@ -45,10 +45,8 @@ class CreateCommand extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * @return array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/system/console/CreateMigration.php b/modules/system/console/CreateMigration.php index 811df264f3..a06bc4f11d 100644 --- a/modules/system/console/CreateMigration.php +++ b/modules/system/console/CreateMigration.php @@ -57,10 +57,8 @@ class CreateMigration extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * @return array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/system/console/CreateModel.php b/modules/system/console/CreateModel.php index 34d4e10b5a..5771a52d9e 100644 --- a/modules/system/console/CreateModel.php +++ b/modules/system/console/CreateModel.php @@ -87,10 +87,8 @@ public function handle() /** * Prepare variables for stubs. - * - * @return array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/system/console/CreatePlugin.php b/modules/system/console/CreatePlugin.php index b4ecd018e4..7c5934f414 100644 --- a/modules/system/console/CreatePlugin.php +++ b/modules/system/console/CreatePlugin.php @@ -39,38 +39,33 @@ class CreatePlugin extends BaseScaffoldCommand 'scaffold/plugin/version.stub' => 'updates/version.yaml', ]; + /** + * @var bool Validate the provided plugin input against the PluginManager, default true. + */ + protected $validatePluginInput = false; + /** * Prepare variables for stubs. - * - * @return array */ - protected function prepareVars() + protected function prepareVars(): array { - /* - * Extract the author and name from the plugin code - */ - $parts = explode('.', $this->getNameInput()); - - if (count($parts) !== 2) { - $this->error('Invalid plugin name, either too many dots or not enough.'); - $this->error('Example name: AuthorName.PluginName'); - return; - } - - $plugin = array_pop($parts); - $author = array_pop($parts); + $vars = parent::prepareVars(); + $pluginCode = $this->getPluginIdentifier(); + $vars['name'] = explode('.', $pluginCode)[1]; - return [ - 'name' => $plugin, - 'author' => $author, - ]; + return $vars; } /** - * Don't suggest any values, this command is for creating plugins + * Gets the localization keys and values to be stored in the plugin's localization files + * Can reference $this->vars and $this->laravel->getLocale() internally */ - public function suggestPluginValues(): array + protected function getLangKeys(): array { - return []; + return [ + 'plugin.name' => $this->vars['name'], + 'plugin.description' => 'No description provided yet...', + 'permissions.some_permission' => 'Some permission', + ]; } } diff --git a/modules/system/console/CreateSettings.php b/modules/system/console/CreateSettings.php index c61ced7d97..aff48cf5c8 100644 --- a/modules/system/console/CreateSettings.php +++ b/modules/system/console/CreateSettings.php @@ -42,10 +42,8 @@ class CreateSettings extends BaseScaffoldCommand /** * Prepare variables for stubs. - * - * @return array */ - protected function prepareVars() + protected function prepareVars(): array { $parts = explode('.', $this->getPluginIdentifier()); $plugin = array_pop($parts); diff --git a/modules/system/console/scaffold/plugin/plugin.stub b/modules/system/console/scaffold/plugin/plugin.stub index 509ac6e1a5..ed3b2d5e32 100644 --- a/modules/system/console/scaffold/plugin/plugin.stub +++ b/modules/system/console/scaffold/plugin/plugin.stub @@ -1,96 +1,84 @@ - '{{name}}', - 'description' => 'No description provided yet...', - 'author' => '{{author}}', - 'icon' => 'icon-leaf', + 'name' => '{{ plugin_id }}::lang.plugin.name', + 'description' => '{{ plugin_id }}::lang.plugin.description', + 'author' => '{{ author }}', + 'icon' => 'icon-leaf' ]; } /** * Register method, called when the plugin is first registered. - * - * @return void */ - public function register() + public function register(): void { } /** * Boot method, called right before the request route. - * - * @return array */ - public function boot() + public function boot(): void { } /** - * Registers any front-end components implemented in this plugin. - * - * @return array + * Registers any frontend components implemented in this plugin. */ - public function registerComponents() + public function registerComponents(): array { return []; // Remove this line to activate return [ - '{{studly_author}}\{{studly_name}}\Components\MyComponent' => 'myComponent', + '{{ plugin_namespace }}\Components\MyComponent' => 'myComponent', ]; } /** - * Registers any back-end permissions used by this plugin. - * - * @return array + * Registers any backend permissions used by this plugin. */ - public function registerPermissions() + public function registerPermissions(): array { return []; // Remove this line to activate return [ - '{{lower_author}}.{{lower_name}}.some_permission' => [ - 'tab' => '{{name}}', - 'label' => 'Some permission', + '{{ plugin_id }}.some_permission' => [ + 'tab' => '{{ plugin_id }}::lang.plugin.name', + 'label' => '{{ plugin_id }}::lang.permissions.some_permission', 'roles' => [UserRole::CODE_DEVELOPER, UserRole::CODE_PUBLISHER], ], ]; } /** - * Registers back-end navigation items for this plugin. - * - * @return array + * Registers backend navigation items for this plugin. */ - public function registerNavigation() + public function registerNavigation(): array { return []; // Remove this line to activate return [ - '{{lower_name}}' => [ - 'label' => '{{name}}', - 'url' => Backend::url('{{lower_author}}/{{lower_name}}/mycontroller'), + '{{ lower_name }}' => [ + 'label' => '{{ plugin_id }}::lang.plugin.name', + 'url' => Backend::url('{{ plugin_url }}/mycontroller'), 'icon' => 'icon-leaf', - 'permissions' => ['{{lower_author}}.{{lower_name}}.*'], + 'permissions' => ['{{ plugin_id }}.*'], 'order' => 500, ], ]; diff --git a/modules/system/console/scaffold/plugin/version.stub b/modules/system/console/scaffold/plugin/version.stub index 326445e362..57b822d815 100644 --- a/modules/system/console/scaffold/plugin/version.stub +++ b/modules/system/console/scaffold/plugin/version.stub @@ -1 +1,2 @@ -"1.0.0": "First version of {{name}}" +'1.0.0': + - 'First version of {{ name }}' diff --git a/modules/system/console/traits/HasPluginArgument.php b/modules/system/console/traits/HasPluginArgument.php index 0f743c3438..75978417df 100644 --- a/modules/system/console/traits/HasPluginArgument.php +++ b/modules/system/console/traits/HasPluginArgument.php @@ -16,6 +16,11 @@ trait HasPluginArgument */ // protected $hasPluginsFilter = 'enabled'; + /** + * @var bool Validate the provided plugin input against the PluginManager, default true. + */ + // protected $validatePluginInput = true; + /** * Return available plugins for autocompletion of the "plugin" argument */ @@ -52,7 +57,10 @@ public function getPluginIdentifier($identifier = null): string $pluginName = $identifier ?? $this->argument('plugin'); $pluginName = $pluginManager->normalizeIdentifier($pluginName); - if (!$pluginManager->hasPlugin($pluginName)) { + if ( + (isset($this->validatePluginInput) && $this->validatePluginInput !== false) + && !$pluginManager->hasPlugin($pluginName) + ) { throw new InvalidArgumentException(sprintf('Plugin "%s" could not be found.', $pluginName)); } From a02c33489d76b97338f1a9da242dcf9a9873ff0c Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Mar 2022 15:11:49 -0600 Subject: [PATCH 04/11] Add localization support to create:controller --- modules/backend/console/CreateController.php | 38 ++++++++++++++----- .../scaffold/controller/_list_toolbar.stub | 8 ++-- .../scaffold/controller/config_form.stub | 16 ++++---- .../scaffold/controller/config_list.stub | 8 ++-- .../scaffold/controller/controller.stub | 8 ++-- .../console/scaffold/controller/create.stub | 14 +++---- .../console/scaffold/controller/index.stub | 1 - .../console/scaffold/controller/preview.stub | 4 +- .../console/scaffold/controller/update.stub | 16 ++++---- 9 files changed, 65 insertions(+), 48 deletions(-) diff --git a/modules/backend/console/CreateController.php b/modules/backend/console/CreateController.php index b0424d3100..b1acd0b245 100644 --- a/modules/backend/console/CreateController.php +++ b/modules/backend/console/CreateController.php @@ -53,25 +53,43 @@ class CreateController extends BaseScaffoldCommand */ protected function prepareVars(): array { - $parts = explode('.', $this->getPluginIdentifier()); - $plugin = array_pop($parts); - $author = array_pop($parts); - $name = $this->getNameInput(); - + $vars = parent::prepareVars(); /* * Determine the model name to use, * either supplied or singular from the controller name. */ $model = $this->option('model'); if (!$model) { - $model = Str::singular($name); + $model = Str::singular($vars['name']); } + $vars['model']; + + return $vars; + } + + /** + * Adds controller & model lang helpers to the vars + */ + protected function processVars($vars): array + { + $vars = parent::processVars($vars); + $vars['controller_url'] = "{$vars['plugin_url']}/{$vars['lower_name']}"; + $vars['model_lang_key_short'] = "models.{$vars['lower_model']}"; + $vars['model_lang_key'] = "{$vars['plugin_id']}::lang.{$vars['model_lang_key_short']}"; + + return $vars; + } + + /** + * Gets the localization keys and values to be stored in the plugin's localization files + * Can reference $this->vars and $this->laravel->getLocale() internally + */ + protected function getLangKeys(): array + { return [ - 'name' => $name, - 'model' => $model, - 'author' => $author, - 'plugin' => $plugin, + "{$this->vars['model_lang_key_short']}.label" => $this->vars['title_singular_name'], + "{$this->vars['model_lang_key_short']}.label_plural" => $this->vars['title_plural_name'], ]; } } diff --git a/modules/backend/console/scaffold/controller/_list_toolbar.stub b/modules/backend/console/scaffold/controller/_list_toolbar.stub index 839d456519..f062a1c33c 100644 --- a/modules/backend/console/scaffold/controller/_list_toolbar.stub +++ b/modules/backend/console/scaffold/controller/_list_toolbar.stub @@ -1,8 +1,8 @@
- New {{title_singular_name}} + trans('{{ model_lang_key }}.label')])); ?>
diff --git a/modules/backend/console/scaffold/controller/config_form.stub b/modules/backend/console/scaffold/controller/config_form.stub index f5b0f4fe57..27320fca0c 100644 --- a/modules/backend/console/scaffold/controller/config_form.stub +++ b/modules/backend/console/scaffold/controller/config_form.stub @@ -3,28 +3,28 @@ # =================================== # Record name -name: {{title_singular_name}} +name: '{{ model_lang_key }}.label' # Model Form Field configuration -form: $/{{lower_author}}/{{lower_plugin}}/models/{{lower_model}}/fields.yaml +form: $/{{ plugin_folder }}/models/{{ lower_model }}/fields.yaml # Model Class name -modelClass: {{studly_author}}\{{studly_plugin}}\Models\{{studly_model}} +modelClass: {{ plugin_namespace }}\Models\{{ studly_model }} # Default redirect location -defaultRedirect: {{lower_author}}/{{lower_plugin}}/{{lower_name}} +defaultRedirect: {{ controller_url }} # Create page create: title: backend::lang.form.create_title - redirect: {{lower_author}}/{{lower_plugin}}/{{lower_name}}/update/:id - redirectClose: {{lower_author}}/{{lower_plugin}}/{{lower_name}} + redirect: {{ controller_url }}/update/:id + redirectClose: {{ controller_url }} # Update page update: title: backend::lang.form.update_title - redirect: {{lower_author}}/{{lower_plugin}}/{{lower_name}} - redirectClose: {{lower_author}}/{{lower_plugin}}/{{lower_name}} + redirect: {{ controller_url }} + redirectClose: {{ controller_url }} # Preview page preview: diff --git a/modules/backend/console/scaffold/controller/config_list.stub b/modules/backend/console/scaffold/controller/config_list.stub index 6ffce9b3d3..0725e06da8 100644 --- a/modules/backend/console/scaffold/controller/config_list.stub +++ b/modules/backend/console/scaffold/controller/config_list.stub @@ -3,16 +3,16 @@ # =================================== # Model List Column configuration -list: $/{{lower_author}}/{{lower_plugin}}/models/{{lower_model}}/columns.yaml +list: $/{{ plugin_folder }}/models/{{ lower_model }}/columns.yaml # Model Class name -modelClass: {{studly_author}}\{{studly_plugin}}\Models\{{studly_model}} +modelClass: {{ plugin_namespace }}\Models\{{ studly_model }} # List Title -title: Manage {{title_plural_name}} +title: '{{ model_lang_key }}.label_plural' # Link URL for each record -recordUrl: {{lower_author}}/{{lower_plugin}}/{{lower_name}}/update/:id +recordUrl: {{ controller_url }}/update/:id # Message to display if the list is empty noRecordsMessage: backend::lang.list.no_records diff --git a/modules/backend/console/scaffold/controller/controller.stub b/modules/backend/console/scaffold/controller/controller.stub index 6797a76faf..0c5e1f3da9 100644 --- a/modules/backend/console/scaffold/controller/controller.stub +++ b/modules/backend/console/scaffold/controller/controller.stub @@ -1,12 +1,12 @@ - @@ -19,21 +19,21 @@ type="submit" data-request="onSave" data-hotkey="ctrl+s, cmd+s" - data-load-indicator="Creating {{singular_name}}..." + data-load-indicator=" trans('{{ model_lang_key }}.label')])); ?>" class="btn btn-primary"> - Create + - or Cancel + or @@ -43,6 +43,6 @@

fatalError) ?>

-

Return to {{lower_title_name}} list

+

diff --git a/modules/backend/console/scaffold/controller/index.stub b/modules/backend/console/scaffold/controller/index.stub index 766877d929..ea43a3636c 100644 --- a/modules/backend/console/scaffold/controller/index.stub +++ b/modules/backend/console/scaffold/controller/index.stub @@ -1,2 +1 @@ - listRender() ?> diff --git a/modules/backend/console/scaffold/controller/preview.stub b/modules/backend/console/scaffold/controller/preview.stub index df5524cd45..fbd3d7457d 100644 --- a/modules/backend/console/scaffold/controller/preview.stub +++ b/modules/backend/console/scaffold/controller/preview.stub @@ -1,6 +1,6 @@ @@ -14,6 +14,6 @@

fatalError) ?>

-

Return to {{lower_title_name}} list

+

diff --git a/modules/backend/console/scaffold/controller/update.stub b/modules/backend/console/scaffold/controller/update.stub index 7812f2b748..4d68a5571a 100644 --- a/modules/backend/console/scaffold/controller/update.stub +++ b/modules/backend/console/scaffold/controller/update.stub @@ -20,28 +20,28 @@ data-request="onSave" data-request-data="redirect:0" data-hotkey="ctrl+s, cmd+s" - data-load-indicator="Saving {{singular_name}}..." + data-load-indicator=" trans('{{ model_lang_key }}.label')])); ?>" class="btn btn-primary"> - Save + - or Cancel + or @@ -51,6 +51,6 @@

fatalError) ?>

-

Return to {{lower_title_name}} list

+

From 00f721c3518bb395627d54308b3f90a41b09c47a Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Mar 2022 15:18:28 -0600 Subject: [PATCH 05/11] Localized create:model scaffolding --- modules/system/console/CreateModel.php | 28 ++++++++++++------- .../console/scaffold/model/columns.stub | 13 ++++++++- .../console/scaffold/model/create_table.stub | 4 +-- .../system/console/scaffold/model/fields.stub | 2 +- .../system/console/scaffold/model/model.stub | 8 +++--- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/modules/system/console/CreateModel.php b/modules/system/console/CreateModel.php index 5771a52d9e..7a1097217b 100644 --- a/modules/system/console/CreateModel.php +++ b/modules/system/console/CreateModel.php @@ -1,6 +1,6 @@ getPluginIdentifier()); - $plugin = array_pop($parts); - $author = array_pop($parts); - $name = $this->getNameInput(); + $vars = parent::processVars($vars); + $vars['table_name'] = "{$vars['lower_author']}_{$vars['lower_plugin']}_{$vars['snake_plural_name']}"; + + return $vars; + } + + /** + * Gets the localization keys and values to be stored in the plugin's localization files + * Can reference $this->vars and $this->laravel->getLocale() internally + */ + protected function getLangKeys(): array + { return [ - 'name' => $name, - 'author' => $author, - 'plugin' => $plugin, + 'models.general.id' => 'ID', + 'models.general.created_at' => 'Created At', + 'models.general.updated_at' => 'Updated At', ]; } diff --git a/modules/system/console/scaffold/model/columns.stub b/modules/system/console/scaffold/model/columns.stub index b11160b422..a301943da8 100644 --- a/modules/system/console/scaffold/model/columns.stub +++ b/modules/system/console/scaffold/model/columns.stub @@ -4,5 +4,16 @@ columns: id: - label: ID + label: '{{ plugin_id }}::lang.models.general.id' searchable: true + created_at: + label: '{{ plugin_id }}::lang.models.general.created_at' + type: datetime + searchable: true + sortable: true + invisible: true + updated_at: + label: '{{ plugin_id }}::lang.models.general.updated_at' + type: datetime + searchable: true + sortable: true diff --git a/modules/system/console/scaffold/model/create_table.stub b/modules/system/console/scaffold/model/create_table.stub index b2a280c091..49410532ce 100644 --- a/modules/system/console/scaffold/model/create_table.stub +++ b/modules/system/console/scaffold/model/create_table.stub @@ -7,7 +7,7 @@ return new class extends Migration { public function up() { - Schema::create('{{lower_author}}_{{lower_plugin}}_{{snake_plural_name}}', function (Blueprint $table) { + Schema::create('{{ table_name }}', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->timestamps(); @@ -16,6 +16,6 @@ return new class extends Migration public function down() { - Schema::dropIfExists('{{lower_author}}_{{lower_plugin}}_{{snake_plural_name}}'); + Schema::dropIfExists('{{ table_name }}'); } }; diff --git a/modules/system/console/scaffold/model/fields.stub b/modules/system/console/scaffold/model/fields.stub index c611f31c78..b0ffc32917 100644 --- a/modules/system/console/scaffold/model/fields.stub +++ b/modules/system/console/scaffold/model/fields.stub @@ -4,5 +4,5 @@ fields: id: - label: ID + label: '{{ plugin_id }}::lang.models.general.id' disabled: true diff --git a/modules/system/console/scaffold/model/model.stub b/modules/system/console/scaffold/model/model.stub index 421cad0013..49615c049f 100644 --- a/modules/system/console/scaffold/model/model.stub +++ b/modules/system/console/scaffold/model/model.stub @@ -1,18 +1,18 @@ - Date: Wed, 23 Mar 2022 15:32:08 -0600 Subject: [PATCH 06/11] Simplify prepareVars usage, fix create:theme usage --- modules/backend/console/CreateFormWidget.php | 17 ------ .../backend/console/CreateReportWidget.php | 17 ------ modules/cms/console/CreateComponent.php | 17 ------ modules/cms/console/CreateTheme.php | 54 +++++++------------ modules/system/console/CreatePlugin.php | 10 ++-- modules/system/console/CreateSettings.php | 15 ++---- 6 files changed, 25 insertions(+), 105 deletions(-) diff --git a/modules/backend/console/CreateFormWidget.php b/modules/backend/console/CreateFormWidget.php index 86db87a67c..32475e47d1 100644 --- a/modules/backend/console/CreateFormWidget.php +++ b/modules/backend/console/CreateFormWidget.php @@ -51,21 +51,4 @@ class CreateFormWidget extends BaseScaffoldCommand 'scaffold/formwidget/stylesheet.stub' => 'formwidgets/{{lower_name}}/assets/css/{{lower_name}}.css', 'scaffold/formwidget/javascript.stub' => 'formwidgets/{{lower_name}}/assets/js/{{lower_name}}.js', ]; - - /** - * Prepare variables for stubs. - */ - protected function prepareVars(): array - { - $parts = explode('.', $this->getPluginIdentifier()); - $plugin = array_pop($parts); - $author = array_pop($parts); - $name = $this->getNameInput(); - - return [ - 'name' => $name, - 'author' => $author, - 'plugin' => $plugin, - ]; - } } diff --git a/modules/backend/console/CreateReportWidget.php b/modules/backend/console/CreateReportWidget.php index a3c22885a9..e5218fa2b4 100644 --- a/modules/backend/console/CreateReportWidget.php +++ b/modules/backend/console/CreateReportWidget.php @@ -49,21 +49,4 @@ class CreateReportWidget extends BaseScaffoldCommand 'scaffold/reportwidget/reportwidget.stub' => 'reportwidgets/{{studly_name}}.php', 'scaffold/reportwidget/widget.stub' => 'reportwidgets/{{lower_name}}/partials/_{{lower_name}}.htm', ]; - - /** - * Prepare variables for stubs. - */ - protected function prepareVars(): array - { - $parts = explode('.', $this->getPluginIdentifier()); - $plugin = array_pop($parts); - $author = array_pop($parts); - $name = $this->getNameInput(); - - return [ - 'name' => $name, - 'author' => $author, - 'plugin' => $plugin, - ]; - } } diff --git a/modules/cms/console/CreateComponent.php b/modules/cms/console/CreateComponent.php index 8e829de9b4..da9af3e3f2 100644 --- a/modules/cms/console/CreateComponent.php +++ b/modules/cms/console/CreateComponent.php @@ -49,21 +49,4 @@ class CreateComponent extends BaseScaffoldCommand 'scaffold/component/component.stub' => 'components/{{studly_name}}.php', 'scaffold/component/default.stub' => 'components/{{lower_name}}/default.htm', ]; - - /** - * Prepare variables for stubs. - */ - protected function prepareVars(): array - { - $parts = explode('.', $this->getPluginIdentifier()); - $plugin = array_pop($parts); - $author = array_pop($parts); - $name = $this->getNameInput(); - - return [ - 'name' => $name, - 'author' => $author, - 'plugin' => $plugin, - ]; - } } diff --git a/modules/cms/console/CreateTheme.php b/modules/cms/console/CreateTheme.php index 9f8e702d6c..70bb26b160 100644 --- a/modules/cms/console/CreateTheme.php +++ b/modules/cms/console/CreateTheme.php @@ -1,37 +1,28 @@ (eg: MyTheme)} {--force : Overwrite existing files with generated files.}'; /** - * The console command description. - * - * @var string + * @var string The console command description. */ protected $description = 'Creates a new theme.'; /** - * The type of class being generated. - * - * @var string + * @var string The type of class being generated. */ protected $type = 'Theme'; @@ -41,9 +32,7 @@ class CreateTheme extends BaseScaffoldCommand protected $nameFrom = 'theme'; /** - * A mapping of stub to generated file. - * - * @var array + * @var array A mapping of stub to generated file. */ protected $stubs = [ 'scaffold/theme/assets/js/app.stub' => 'assets/js/app.js', @@ -60,18 +49,21 @@ class CreateTheme extends BaseScaffoldCommand 'scaffold/theme/version.stub' => 'version.yaml', ]; + /** + * Get the desired class name from the input. + */ + protected function getNameInput(): string + { + return str_slug(parent::getNameInput()); + } + /** * Prepare variables for stubs. */ protected function prepareVars(): array { - /* - * Extract the author and name from the plugin code - */ - $code = str_slug($this->getNameInput()); - return [ - 'code' => $code, + 'code' => $this->getNameInput(), ]; } @@ -80,9 +72,7 @@ protected function prepareVars(): array */ protected function getDestinationPath(): string { - $code = $this->prepareVars()['code']; - - return themes_path($code); + return themes_path($this->getNameInput()); } /** @@ -97,11 +87,12 @@ public function makeStub($stubName) } $sourceFile = $this->getSourcePath() . '/' . $stubName; - $destinationFile = $this->getDestinationPath() . '/' . $this->stubs[$stubName]; + $destinationFile = $this->getDestinationForStub($stubName); $destinationContent = $this->files->get($sourceFile); /* * Parse each variable in to the destination content and path + * @NOTE: CANNOT USE TWIG AS IT WOULD CONFLICT WITH THE TWIG TEMPLATES THEMSELVES */ foreach ($this->vars as $key => $var) { $destinationContent = str_replace('{{' . $key . '}}', $var, $destinationContent); @@ -110,13 +101,6 @@ public function makeStub($stubName) $this->makeDirectory($destinationFile); - /* - * Make sure this file does not already exist - */ - if ($this->files->exists($destinationFile) && !$this->option('force')) { - throw new Exception('Stop everything!!! This file already exists: ' . $destinationFile); - } - $this->files->put($destinationFile, $destinationContent); } } diff --git a/modules/system/console/CreatePlugin.php b/modules/system/console/CreatePlugin.php index 7c5934f414..6692739ea8 100644 --- a/modules/system/console/CreatePlugin.php +++ b/modules/system/console/CreatePlugin.php @@ -45,15 +45,11 @@ class CreatePlugin extends BaseScaffoldCommand protected $validatePluginInput = false; /** - * Prepare variables for stubs. + * Get the desired class name from the input. */ - protected function prepareVars(): array + protected function getNameInput(): string { - $vars = parent::prepareVars(); - $pluginCode = $this->getPluginIdentifier(); - $vars['name'] = explode('.', $pluginCode)[1]; - - return $vars; + return explode('.', $this->getPluginIdentifier())[1]; } /** diff --git a/modules/system/console/CreateSettings.php b/modules/system/console/CreateSettings.php index aff48cf5c8..b4d4b50080 100644 --- a/modules/system/console/CreateSettings.php +++ b/modules/system/console/CreateSettings.php @@ -41,19 +41,10 @@ class CreateSettings extends BaseScaffoldCommand ]; /** - * Prepare variables for stubs. + * Get the desired class name from the input. */ - protected function prepareVars(): array + protected function getNameInput(): string { - $parts = explode('.', $this->getPluginIdentifier()); - $plugin = array_pop($parts); - $author = array_pop($parts); - $name = $this->getNameInput() ?? 'Settings'; - - return [ - 'name' => $name, - 'author' => $author, - 'plugin' => $plugin, - ]; + return parent::getNameInput() ?? 'Settings'; } } From 574437b9bed3b05f233bb11720c248b6f84155e6 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Sun, 3 Apr 2022 13:40:18 -0600 Subject: [PATCH 07/11] Moved GeneratorCommand back to Scaffold --- modules/cms/console/CreateTheme.php | 2 +- modules/system/console/BaseScaffoldCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/cms/console/CreateTheme.php b/modules/cms/console/CreateTheme.php index 70bb26b160..bcfb3b162b 100644 --- a/modules/cms/console/CreateTheme.php +++ b/modules/cms/console/CreateTheme.php @@ -1,6 +1,6 @@ Date: Sun, 3 Apr 2022 13:53:26 -0600 Subject: [PATCH 08/11] Plugins should be generated the same no matter what OS is being used to generate them --- modules/system/console/BaseScaffoldCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system/console/BaseScaffoldCommand.php b/modules/system/console/BaseScaffoldCommand.php index 47c8b41b0d..57e6cdb8bc 100644 --- a/modules/system/console/BaseScaffoldCommand.php +++ b/modules/system/console/BaseScaffoldCommand.php @@ -43,7 +43,7 @@ protected function processVars(array $vars): array $vars['plugin_id'] = "{$vars['lower_author']}.{$vars['lower_plugin']}"; $vars['plugin_code'] = "{$vars['studly_author']}.{$vars['studly_plugin']}"; $vars['plugin_url'] = "{$vars['lower_author']}/{$vars['lower_plugin']}"; - $vars['plugin_folder'] = "{$vars['lower_author']}" . DIRECTORY_SEPARATOR . "{$vars['lower_plugin']}"; + $vars['plugin_folder'] = "{$vars['lower_author']}/{$vars['lower_plugin']}"; $vars['plugin_namespace'] = "{$vars['studly_author']}\\{$vars['studly_plugin']}"; return $vars; From e2e2fc428d76ca1d1bfd5e9ae6010f649edbad18 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Sun, 3 Apr 2022 14:20:54 -0600 Subject: [PATCH 09/11] Stub out create:migration --- modules/system/ServiceProvider.php | 1 + modules/system/console/CreateMigration.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/system/ServiceProvider.php b/modules/system/ServiceProvider.php index 1bc573b359..35dc3847aa 100644 --- a/modules/system/ServiceProvider.php +++ b/modules/system/ServiceProvider.php @@ -243,6 +243,7 @@ protected function registerConsole() * Register console commands */ $this->registerConsoleCommand('create.command', \System\Console\CreateCommand::class); + $this->registerConsoleCommand('create.migration', \System\Console\CreateMigration::class); $this->registerConsoleCommand('create.model', \System\Console\CreateModel::class); $this->registerConsoleCommand('create.plugin', \System\Console\CreatePlugin::class); $this->registerConsoleCommand('create.settings', \System\Console\CreateSettings::class); diff --git a/modules/system/console/CreateMigration.php b/modules/system/console/CreateMigration.php index a06bc4f11d..98835756b7 100644 --- a/modules/system/console/CreateMigration.php +++ b/modules/system/console/CreateMigration.php @@ -25,7 +25,7 @@ class CreateMigration extends BaseScaffoldCommand {plugin : The name of the plugin. (eg: Winter.Blog)} {name : The name of the migration to generate. (eg: CreatePostTable)} {--f|force : Overwrite existing files with generated files.} - {--m|model : The model to create a migration for. (eg: Post)} + {--model= : The model to create a migration for. (eg: Post)} {--table= : The table to migrate, defaults to autogenerated from the provided model. (eg: winter_blog_posts)} {--c|create : Generate a migration that creates the specified table} {--u|update : Generate a migration that updates the specified table} @@ -68,13 +68,13 @@ protected function prepareVars(): array $model = $this->option('model'); if (empty($table) && !empty($model)) { - $modelClass = "\{$author}\{$plugin}\Models\{$model}"; + $modelClass = "\\{$author}\\{$plugin}\Models\\{$model}"; if (class_exists($modelClass)) { $table = (new $modelClass)->getTable(); } } - dd($author, $plugin, $name, $table, $model); + $this->error("create:migration has not been implemented yet"); return [ 'name' => $name, From f3c9a257423e9ac1ddca8a52e334456a8496c63f Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Sun, 3 Apr 2022 14:31:27 -0600 Subject: [PATCH 10/11] Fix create:controller command --- modules/backend/console/CreateController.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/backend/console/CreateController.php b/modules/backend/console/CreateController.php index b1acd0b245..c853ab8ca6 100644 --- a/modules/backend/console/CreateController.php +++ b/modules/backend/console/CreateController.php @@ -3,6 +3,10 @@ use System\Console\BaseScaffoldCommand; use Winter\Storm\Support\Str; +/** + * @TODO: + * - Support creating related permissions and navigation items and injecting them into the plugin + */ class CreateController extends BaseScaffoldCommand { /** @@ -62,7 +66,7 @@ protected function prepareVars(): array if (!$model) { $model = Str::singular($vars['name']); } - $vars['model']; + $vars['model'] = $model; return $vars; } From 6fd53d4e180bf12cc822d53f851f84557cf45a53 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Sun, 3 Apr 2022 14:47:20 -0600 Subject: [PATCH 11/11] Improve Settings scaffolding --- modules/system/console/CreateSettings.php | 2 +- modules/system/console/scaffold/settings/model.stub | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/system/console/CreateSettings.php b/modules/system/console/CreateSettings.php index b4d4b50080..6019aa58e0 100644 --- a/modules/system/console/CreateSettings.php +++ b/modules/system/console/CreateSettings.php @@ -45,6 +45,6 @@ class CreateSettings extends BaseScaffoldCommand */ protected function getNameInput(): string { - return parent::getNameInput() ?? 'Settings'; + return parent::getNameInput() ?: 'Settings'; } } diff --git a/modules/system/console/scaffold/settings/model.stub b/modules/system/console/scaffold/settings/model.stub index 740e775a93..002b8a20e5 100644 --- a/modules/system/console/scaffold/settings/model.stub +++ b/modules/system/console/scaffold/settings/model.stub @@ -12,7 +12,7 @@ class {{studly_name}} extends Model /** * @var array Behaviors implemented by this model. */ - public $implement = ['System.Behaviors.SettingsModel']; + public $implement = [\System\Behaviors\SettingsModel::class]; /** * @var string Unique code