diff --git a/CHANGELOG.md b/CHANGELOG.md index fe35048f1..76b9e4d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - **Medium Impact Changes** - [spiral/core] Interface `Spiral\Core\Container\SingletonInterface` is deprecated, use `Spiral\Core\Attribute\Singleton` instead. Will be removed in v4.0. +- **Other Features** + - Added `Spiral\Scaffolder\Command\InfoCommand` console command for getting information about available scaffolder + commands. ## 3.11.1 - 2023-12-29 diff --git a/src/Scaffolder/src/Bootloader/ScaffolderBootloader.php b/src/Scaffolder/src/Bootloader/ScaffolderBootloader.php index f1519708f..0bc0afb20 100644 --- a/src/Scaffolder/src/Bootloader/ScaffolderBootloader.php +++ b/src/Scaffolder/src/Bootloader/ScaffolderBootloader.php @@ -9,6 +9,7 @@ use ReflectionClass; use ReflectionException; use Spiral\Boot\Bootloader\Bootloader; +use Spiral\Boot\DirectoriesInterface; use Spiral\Boot\KernelInterface; use Spiral\Config\ConfiguratorInterface; use Spiral\Config\Patch\Append; @@ -29,8 +30,9 @@ public function __construct( ) { } - public function init(ConsoleBootloader $console): void + public function init(ConsoleBootloader $console, DirectoriesInterface $dir): void { + $console->addCommand(Command\InfoCommand::class); $console->addCommand(Command\BootloaderCommand::class); $console->addCommand(Command\CommandCommand::class); $console->addCommand(Command\ConfigCommand::class); @@ -56,7 +58,7 @@ public function init(ConsoleBootloader $console): void * Base directory for generated classes, class will be automatically localed into sub directory * using given namespace. */ - 'directory' => directory('app') . 'src/', + 'directory' => $dir->get('app') . 'src/', /* * Default namespace to be applied for every generated class. By default uses Kernel namespace @@ -83,7 +85,7 @@ public function init(ConsoleBootloader $console): void 'postfix' => 'Config', 'class' => Declaration\ConfigDeclaration::class, 'options' => [ - 'directory' => directory('config'), + 'directory' => $dir->get('config'), ], ], Declaration\ControllerDeclaration::TYPE => [ @@ -98,7 +100,7 @@ public function init(ConsoleBootloader $console): void ], Declaration\MiddlewareDeclaration::TYPE => [ 'namespace' => 'Middleware', - 'postfix' => '', + 'postfix' => 'Middleware', 'class' => Declaration\MiddlewareDeclaration::class, ], Declaration\CommandDeclaration::TYPE => [ @@ -117,7 +119,7 @@ public function init(ConsoleBootloader $console): void } /** - * Register new Scaffolder declaration. + * Register a new Scaffolder declaration. * * @param non-empty-string $name */ diff --git a/src/Scaffolder/src/Command/InfoCommand.php b/src/Scaffolder/src/Command/InfoCommand.php new file mode 100644 index 000000000..b959e59f9 --- /dev/null +++ b/src/Scaffolder/src/Command/InfoCommand.php @@ -0,0 +1,84 @@ +output->title('Scaffolder commands'); + $this->writeln( + 'Scaffolder enables developers to quickly and easily generate application code for various classes, using a set of console commands', + ); + + $this->newLine(); + + $table = $this->table(['Command', 'Target']); + $rootDir = $dirs->get('root'); + $available = $config->getDeclarations(); + + $i = 0; + foreach ($available as $name) { + $command = 'create:' . $name; + + if (!$console->getApplication()->has($command)) { + continue; + } + + $command = $console->getApplication()->get($command); + + if ($i > 0) { + $table->addRow(new TableSeparator()); + } + $declaration = $config->getDeclaration($name); + + $options = []; + foreach ($declaration['options'] ?? [] as $key => $value) { + $options[] = $key . ': ' . \json_encode(\str_replace($rootDir, '', $value)) . ''; + } + + $file = \str_replace($rootDir, '', $config->classFilename($name, $this->name)); + $namespace = $config->classNamespace($name, $this->name); + $table->addRow([ + $command->getName() . "\n{$command->getDescription()}", + <</$file +namespace: $namespace +TARGET + . + ($options !== [] ? "\n" . \implode("\n", $options) : ''), + ]); + + $i++; + } + + $randomName = $available[\array_rand($available)]; + $this->writeln( + "Use `php app.php create:{$randomName} {$this->name}` command to generate desired class. Below is a list of available commands:", + ); + + $table->render(); + + $this->writeln( + 'Use `php app.php create:*** --help` command to see available options.', + ); + + $this->writeln('Read more about scaffolder in https://spiral.dev/docs/basics-scaffolding documentation section.'); + + return self::SUCCESS; + } +} diff --git a/src/Scaffolder/src/Config/ScaffolderConfig.php b/src/Scaffolder/src/Config/ScaffolderConfig.php index 49bf2c8a6..c92e48fb4 100644 --- a/src/Scaffolder/src/Config/ScaffolderConfig.php +++ b/src/Scaffolder/src/Config/ScaffolderConfig.php @@ -16,11 +16,11 @@ class ScaffolderConfig extends InjectableConfig public const CONFIG = 'scaffolder'; protected array $config = [ - 'header' => [], - 'directory' => '', - 'namespace' => '', + 'header' => [], + 'directory' => '', + 'namespace' => '', 'declarations' => [], - 'defaults' => [ + 'defaults' => [ 'declarations' => [], ], ]; @@ -38,6 +38,16 @@ public function baseDirectory(): string return $this->config['directory']; } + /** + * @return non-empty-string[] + */ + public function getDeclarations(): array + { + return \array_keys($this->config['defaults']['declarations'] ?? []) + \array_keys( + $this->config['declarations'], + ); + } + /** * @param non-empty-string $element */ @@ -109,7 +119,7 @@ public function declarationClass(string $element): string if (empty($class)) { throw new ScaffolderException( - \sprintf("Unable to scaffold '%s', no declaration class found", $element) + \sprintf("Unable to scaffold '%s', no declaration class found", $element), ); } @@ -126,6 +136,19 @@ public function declarationOptions(string $element): array return $this->getOption($element, 'options', []); } + /** + * Get declaration options by element name. + * + * @param non-empty-string $element + */ + public function getDeclaration(string $element): array + { + $default = $this->config['defaults']['declarations'][$element] ?? []; + $declaration = $this->config['declarations'][$element] ?? []; + + return $declaration + $default; + } + /** * @param non-empty-string $element */ @@ -183,7 +206,7 @@ private function baseNamespace(string $element): string $declaration = $this->getDeclaration($element); if (\array_key_exists('baseNamespace', $declaration)) { - return \trim((string) $this->getOption($element, 'baseNamespace', ''), '\\'); + return \trim((string)$this->getOption($element, 'baseNamespace', ''), '\\'); } return \trim($this->config['namespace'], '\\'); @@ -211,15 +234,4 @@ private function classify(string $name): string ->build() ->classify($name); } - - /** - * @param non-empty-string $element - */ - private function getDeclaration(string $element): array - { - $default = $this->config['defaults']['declarations'][$element] ?? []; - $declaration = $this->config['declarations'][$element] ?? []; - - return $declaration + $default; - } } diff --git a/src/Scaffolder/tests/Command/InfoCommandTest.php b/src/Scaffolder/tests/Command/InfoCommandTest.php new file mode 100644 index 000000000..f0d05433e --- /dev/null +++ b/src/Scaffolder/tests/Command/InfoCommandTest.php @@ -0,0 +1,30 @@ +console()->run('scaffolder:info')->getOutput()->fetch(); + + $strings = [ + 'Scaffolder commands', + 'create:controller', + 'create:bootloader', + 'create:config', + 'create:filter', + 'create:command', + 'create:middleware', + 'create:jobHandler', + ]; + + foreach ($strings as $string) { + $this->assertStringContainsString($string, $result); + } + } +} diff --git a/tests/Framework/Bootloader/Scaffolder/ScaffolderBootloaderTest.php b/tests/Framework/Bootloader/Scaffolder/ScaffolderBootloaderTest.php index da1ee147a..2cd3edf4e 100644 --- a/tests/Framework/Bootloader/Scaffolder/ScaffolderBootloaderTest.php +++ b/tests/Framework/Bootloader/Scaffolder/ScaffolderBootloaderTest.php @@ -32,7 +32,7 @@ public function testAddDeclaration(): void $this->assertSame( ['foo' => ['bar' => 'baz']], - $configs->getConfig(ScaffolderConfig::CONFIG)['defaults']['declarations'] + $configs->getConfig(ScaffolderConfig::CONFIG)['defaults']['declarations'], ); } @@ -73,7 +73,7 @@ public function testDefaultConfig(): void ], Declaration\MiddlewareDeclaration::TYPE => [ 'namespace' => 'Middleware', - 'postfix' => '', + 'postfix' => 'Middleware', 'class' => Declaration\MiddlewareDeclaration::class, ], Declaration\CommandDeclaration::TYPE => [ @@ -87,7 +87,7 @@ public function testDefaultConfig(): void 'class' => Declaration\JobHandlerDeclaration::class, ], ], - ] + ], ], $config); } }