diff --git a/config/config.php b/config/config.php index 32f69c3c3..21f165650 100644 --- a/config/config.php +++ b/config/config.php @@ -41,18 +41,23 @@ 'replacements' => [ /** * Define custom replacements for each section. - * You can specify a closure for dynamic values. + * You can now specify a class name that extends + * \Nwidart\Modules\Support\ReplacementKeyCommand for dynamic values. * * Example: * * 'composer' => [ - * 'CUSTOM_KEY' => fn (\Nwidart\Modules\Generators\ModuleGenerator $generator) => $generator->getModule()->getLowerName() . '-module', - * 'CUSTOM_KEY2' => fn () => 'custom text', + * // Map the UPPERCASE token to your command class + * 'CUSTOM_KEY' => \App\Modules\Support\Replacements\CustomKey::class, + * // You can still list built-in tokens by their names * 'LOWER_NAME', * 'STUDLY_NAME', * // ... * ], * + * The command class must extend ReplacementKeyCommand and implement handle(): string + * to return the replacement text. + * * Note: Keys should be in UPPERCASE. */ 'routes/web' => ['LOWER_NAME', 'STUDLY_NAME', 'PLURAL_LOWER_NAME', 'KEBAB_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'], @@ -132,6 +137,7 @@ 'channels' => ['path' => 'app/Broadcasting', 'generate' => false], 'class' => ['path' => 'app/Classes', 'generate' => false], 'command' => ['path' => 'app/Console', 'generate' => false], + 'command_replacements' => ['path' => 'app/Console/Replacements', 'generate' => false], 'component-class' => ['path' => 'app/View/Components', 'generate' => false], 'emails' => ['path' => 'app/Emails', 'generate' => false], 'event' => ['path' => 'app/Events', 'generate' => false], diff --git a/src/Commands/Make/ReplacementMakeCommand.php b/src/Commands/Make/ReplacementMakeCommand.php new file mode 100644 index 000000000..5c654850e --- /dev/null +++ b/src/Commands/Make/ReplacementMakeCommand.php @@ -0,0 +1,82 @@ +laravel['modules']->getModulePath($this->getModuleName()); + + $filePath = GenerateConfigReader::read('command_replacements')->getPath() ?? config('modules.paths.app_folder').'Console/Replacements'; + + return $path.$filePath.'/'.$this->getFileName().'.php'; + } + + protected function getTemplateContents(): string + { + $module = $this->laravel['modules']->findOrFail($this->getModuleName()); + + return (new Stub($this->getStubName(), [ + 'NAMESPACE' => $this->getClassNamespace($module), + 'CLASS' => $this->getClassNameWithoutNamespace(), + ]))->render(); + } + + protected function getArguments(): array + { + return [ + ['name', InputArgument::REQUIRED, 'The name of the enum class.'], + ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], + ]; + } + + protected function getOptions(): array + { + return [ + ['force', 'f', InputOption::VALUE_NONE, 'su.'], + ]; + } + + protected function getFileName(): string + { + return Str::studly($this->argument('name')); + } + + private function getClassNameWithoutNamespace(): array|string + { + return class_basename($this->getFileName()); + } + + public function getDefaultNamespace(): string + { + return config('modules.paths.generator.command_replacements.namespace') + ?? ltrim(config('modules.paths.generator.command_replacements.path', 'Console/Replacements'), config('modules.paths.app_folder', '')); + } + + protected function getStubName(): string + { + return '/replacement.stub'; + } +} diff --git a/src/Commands/stubs/replacement.stub b/src/Commands/stubs/replacement.stub new file mode 100644 index 000000000..e1ab250f8 --- /dev/null +++ b/src/Commands/stubs/replacement.stub @@ -0,0 +1,13 @@ +generator->getModule()->getLowerName(); + } +} diff --git a/src/Generators/ModuleGenerator.php b/src/Generators/ModuleGenerator.php index 9c731f0ba..da624a643 100644 --- a/src/Generators/ModuleGenerator.php +++ b/src/Generators/ModuleGenerator.php @@ -13,6 +13,7 @@ use Nwidart\Modules\FileRepository; use Nwidart\Modules\Module; use Nwidart\Modules\Support\Config\GenerateConfigReader; +use Nwidart\Modules\Support\ReplacementKeyCommand; use Nwidart\Modules\Support\Stub; use Nwidart\Modules\Traits\PathNamespace; @@ -494,8 +495,8 @@ protected function getReplacement($stub): array } foreach ($keys as $key => $value) { - if ($value instanceof \Closure) { - $replaces[strtoupper($key)] = $value($this); + if (class_exists($value) && is_subclass_of($value, ReplacementKeyCommand::class)) { + $replaces[strtoupper($key)] = (new $value($this))->handle(); } elseif (method_exists($this, $method = 'get'.ucfirst(Str::studly(strtolower($value))).'Replacement')) { $replace = $this->$method(); diff --git a/src/Providers/ConsoleServiceProvider.php b/src/Providers/ConsoleServiceProvider.php index 2448ec31d..da5feb798 100644 --- a/src/Providers/ConsoleServiceProvider.php +++ b/src/Providers/ConsoleServiceProvider.php @@ -79,6 +79,7 @@ public static function defaultCommands(): Collection Commands\Make\ResourceMakeCommand::class, Commands\Make\RouteProviderMakeCommand::class, Commands\Make\RuleMakeCommand::class, + Commands\Make\ReplacementMakeCommand::class, Commands\Make\ScopeMakeCommand::class, Commands\Make\SeedMakeCommand::class, Commands\Make\ServiceMakeCommand::class, diff --git a/src/Support/ReplacementKeyCommand.php b/src/Support/ReplacementKeyCommand.php new file mode 100644 index 000000000..b44d00436 --- /dev/null +++ b/src/Support/ReplacementKeyCommand.php @@ -0,0 +1,12 @@ +finder = $this->app['files']; + $this->createModule(); + $this->modulePath = $this->getModuleAppPath(); + } + + protected function tearDown(): void + { + $this->app[RepositoryInterface::class]->delete('Blog'); + parent::tearDown(); + } + + public function test_it_generates_a_new_replacement_class() + { + $code = $this->artisan('module:make-replacement', ['name' => 'Demo', 'module' => 'Blog']); + + $this->assertTrue(is_file($this->modulePath.'/Console/Replacements/Demo.php')); + $this->assertSame(0, $code); + } + + public function test_it_generates_a_new_replacement_class_can_override_with_force_option() + { + $this->artisan('module:make-replacement', ['name' => 'Demo', 'module' => 'Blog']); + $code = $this->artisan('module:make-replacement', ['name' => 'Demo', 'module' => 'Blog', '--force' => true]); + + $this->assertTrue(is_file($this->modulePath.'/Console/Replacements/Demo.php')); + $this->assertSame(0, $code); + } + + public function test_it_generated_correct_file_with_content() + { + $code = $this->artisan('module:make-replacement', ['name' => 'Demo', 'module' => 'Blog']); + + $file = $this->finder->get($this->modulePath.'/Console/Replacements/Demo.php'); + + $this->assertMatchesSnapshot($file); + $this->assertSame(0, $code); + } + + public function test_it_can_generate_a_replacement_class_in_sub_namespace_in_correct_folder() + { + $code = $this->artisan('module:make-replacement', ['name' => 'Api\\Demo', 'module' => 'Blog']); + + $this->assertTrue(is_file($this->modulePath.'/Console/Replacements/Api/Demo.php')); + $this->assertSame(0, $code); + } + + public function test_it_can_generate_a_replacement_class_in_sub_namespace_with_correct_generated_file() + { + $code = $this->artisan('module:make-replacement', ['name' => 'Api\\Demo', 'module' => 'Blog']); + + $file = $this->finder->get($this->modulePath.'/Console/Replacements/Api/Demo.php'); + + $this->assertMatchesSnapshot($file); + $this->assertSame(0, $code); + } +} diff --git a/tests/Commands/Make/__snapshots__/ReplacementMakeCommandTest__test_it_can_generate_a_replacement_class_in_sub_namespace_with_correct_generated_file__1.txt b/tests/Commands/Make/__snapshots__/ReplacementMakeCommandTest__test_it_can_generate_a_replacement_class_in_sub_namespace_with_correct_generated_file__1.txt new file mode 100644 index 000000000..20bd6c855 --- /dev/null +++ b/tests/Commands/Make/__snapshots__/ReplacementMakeCommandTest__test_it_can_generate_a_replacement_class_in_sub_namespace_with_correct_generated_file__1.txt @@ -0,0 +1,13 @@ +generator->getModule()->getLowerName(); + } +} diff --git a/tests/Commands/Make/__snapshots__/ReplacementMakeCommandTest__test_it_generated_correct_file_with_content__1.txt b/tests/Commands/Make/__snapshots__/ReplacementMakeCommandTest__test_it_generated_correct_file_with_content__1.txt new file mode 100644 index 000000000..0f0cf15af --- /dev/null +++ b/tests/Commands/Make/__snapshots__/ReplacementMakeCommandTest__test_it_generated_correct_file_with_content__1.txt @@ -0,0 +1,13 @@ +generator->getModule()->getLowerName(); + } +}