diff --git a/README.md b/README.md index 11e5779..fff16d8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ extensions: # Generate command for symfony/command package php bin/console worker:command [options] [--] [] +# Generate control with factory and template +php bin/console worker:control [options] [--] [] + # Generate model for nextras/orm package php bin/console worker:orm [options] [--] [ []] diff --git a/src/DI/WorkerExtension.neon b/src/DI/WorkerExtension.neon index 4b87c0b..fb15499 100644 --- a/src/DI/WorkerExtension.neon +++ b/src/DI/WorkerExtension.neon @@ -1,4 +1,5 @@ services: - Adbros\Worker\Jobs\CommandJob + - Adbros\Worker\Jobs\ControlJob - Adbros\Worker\Jobs\OrmJob - Adbros\Worker\Jobs\PresenterJob diff --git a/src/Jobs/ControlJob.php b/src/Jobs/ControlJob.php new file mode 100644 index 0000000..3a79798 --- /dev/null +++ b/src/Jobs/ControlJob.php @@ -0,0 +1,155 @@ +setDescription('Generate control with factory and template.') + ->addArgument('name', InputArgument::OPTIONAL, 'Control name') + ->addOption('namespace', 'ns', InputOption::VALUE_OPTIONAL, 'Control namespace') + ->addOption('control-parent', 'cp', InputOption::VALUE_OPTIONAL, 'Control parent class') + ->addOption('factory-parent', 'fp', InputOption::VALUE_OPTIONAL, 'Factory parent class'); + } + + public function interact(InputInterface $input, SymfonyStyle $io, Command $command): void + { + parent::interact($input, $io, $command); + + if (!$this->isClass($input->getArgument('name'))) { + $name = $io->ask('Enter control name', null, function (?string $answer): string { + if (!$this->isClass($answer)) { + throw new InvalidArgumentException('Please, enter valid control name.'); + } + + return $answer; + }); + + $input->setArgument('name', $name); + } + + if (!$this->isNamespace($input->getOption('namespace'), $input->getOption('root-namespace'))) { + $namespace = $io->ask('Enter control namespace', $input->getOption('root-namespace') . '\\Controls\\' . $input->getArgument('name'), function (?string $answer) use ($input): string { + if (!$this->isNamespace($answer, $input->getOption('root-namespace'))) { + throw new InvalidOptionException('Please, enter valid control namespace.'); + } + + return $answer; + }); + + $input->setOption('namespace', $namespace); + } + + if (!$this->isNamespace($input->getOption('control-parent'))) { + $namespace = $io->ask('Enter entity parent class', 'Nette\\Application\\UI\\Control', function (?string $answer): string { + if (!$this->isNamespace($answer)) { + throw new InvalidOptionException('Please, enter valid control parent class.'); + } + + return $answer; + }); + + $input->setOption('control-parent', $namespace); + } + + if ($input->getOption('factory-parent') !== '' && !$this->isNamespace($input->getOption('factory-parent'))) { + $namespace = $io->ask('Enter factory parent class', '', function (?string $answer): string { + if ($answer !== '' && !$this->isNamespace($answer)) { + throw new InvalidOptionException('Please, enter valid factory parent class.'); + } + + return $answer; + }); + + $input->setOption('factory-parent', $namespace); + } + } + + public function generate(InputInterface $input, SymfonyStyle $io): int + { + $directory = $input->getOption('root-directory') . + $this->namespaceToPath($input->getOption('namespace'), $input->getOption('root-namespace')); + + if (file_exists($directory)) { + throw new RuntimeException(sprintf('Directory %s already exists!', $directory)); + } + + mkdir($directory, 0777, true); + + $file = new PhpFile(); + + if (method_exists($file, 'setStrictTypes')) { + $file->setStrictTypes(true); + } + + $file + ->addNamespace($input->getOption('namespace')) + ->addUse($input->getOption('control-parent')) + ->addClass($input->getArgument('name') . 'Control') + ->setExtends($input->getOption('control-parent')) + ->addMethod('render') + ->setReturnType('void') + ->setBody('$this->template->setFile(\'' . $input->getArgument('name') . 'Control.latte\');' . "\n" . '$this->template->render();'); + + file_put_contents(($filename = $directory . '/' . $input->getArgument('name') . 'Control.php'), (string) $file); + + $io->text(sprintf('File %s created.', $filename)); + + $file = new PhpFile(); + + if (method_exists($file, 'setStrictTypes')) { + $file->setStrictTypes(true); + } + + $namespace = $file + ->addNamespace($input->getOption('namespace')); + + if ($input->getOption('factory-parent') !== '') { + $namespace + ->addUse($input->getOption('factory-parent')); + } + + $class = $namespace + ->addClass($input->getArgument('name') . 'Control'); + + if ($input->getOption('factory-parent') !== '') { + $class + ->setExtends($input->getOption('factory-parent')); + } + + $class->addMethod('create') + ->setReturnType($input->getOption('namespace') . '\\' . $input->getArgument('name') . 'Control') + ->setBody('return new ' . $input->getArgument('name') . 'Control();'); + + file_put_contents(($filename = $directory . '/' . $input->getArgument('name') . 'Factory.php'), (string) $file); + + $io->text(sprintf('File %s created.', $filename)); + + file_put_contents(($filename = $directory . '/' . $input->getArgument('name') . 'Control.latte'), ''); + + $io->text(sprintf('File %s created.', $filename)); + + return 0; + } + +} diff --git a/tests/cases/Jobs/ControlJobTest.phpt b/tests/cases/Jobs/ControlJobTest.phpt new file mode 100644 index 0000000..e8e6931 --- /dev/null +++ b/tests/cases/Jobs/ControlJobTest.phpt @@ -0,0 +1,74 @@ +inputs = [ + '--root-directory' => OUTPUT_DIR, + '--root-namespace' => 'My\\App', + 'name' => 'Test', + '--namespace' => 'My\\App\\My\\Controls\\Test', + '--control-parent' => 'My\\App\\My\\Controls\\BaseControl', + '--factory-parent' => 'My\\App\\My\\Controls\\BaseFactory', + ]; + } + + public function testNoninteractive(): void + { + $fileManager = new FileManager(''); + + $command = new WorkerCommand(new ControlJob($fileManager)); + + $input = new ArrayInput($this->inputs); + + $output = new BufferedOutput(); + + Assert::same(0, $command->run($input, $output)); + + Assert::same(file_get_contents(__DIR__ . '/expected/ControlJob.control.expect'), file_get_contents(OUTPUT_DIR . '/My/Controls/Test/TestControl.php')); + + Assert::same(file_get_contents(__DIR__ . '/expected/ControlJob.factory.expect'), file_get_contents(OUTPUT_DIR . '/My/Controls/Test/TestFactory.php')); + + Assert::same(file_get_contents(__DIR__ . '/expected/ControlJob.template.expect'), file_get_contents(OUTPUT_DIR . '/My/Controls/Test/TestControl.latte')); + } + + public function testInteractive(): void + { + $fileManager = new FileManager(''); + + $command = new WorkerCommand(new ControlJob($fileManager)); + + $input = new ArrayInput([]); + + $input->setStream($this->createStream($this->inputs)); + + $output = new BufferedOutput(); + + Assert::same(0, $command->run($input, $output)); + + Assert::same(file_get_contents(__DIR__ . '/expected/ControlJob.control.expect'), file_get_contents(OUTPUT_DIR . '/My/Controls/Test/TestControl.php')); + + Assert::same(file_get_contents(__DIR__ . '/expected/ControlJob.factory.expect'), file_get_contents(OUTPUT_DIR . '/My/Controls/Test/TestFactory.php')); + + Assert::same(file_get_contents(__DIR__ . '/expected/ControlJob.template.expect'), file_get_contents(OUTPUT_DIR . '/My/Controls/Test/TestControl.latte')); + } + +} + +(new ControlJobTest())->run(); diff --git a/tests/cases/Jobs/expected/ControlJob.control.expect b/tests/cases/Jobs/expected/ControlJob.control.expect new file mode 100644 index 0000000..e5b5688 --- /dev/null +++ b/tests/cases/Jobs/expected/ControlJob.control.expect @@ -0,0 +1,16 @@ +template->setFile('TestControl.latte'); + $this->template->render(); + } +} diff --git a/tests/cases/Jobs/expected/ControlJob.factory.expect b/tests/cases/Jobs/expected/ControlJob.factory.expect new file mode 100644 index 0000000..cb2bade --- /dev/null +++ b/tests/cases/Jobs/expected/ControlJob.factory.expect @@ -0,0 +1,15 @@ +