aboutsummaryrefslogtreecommitdiff
path: root/cli/Commands
diff options
context:
space:
mode:
authorDavid Siegfried <david.siegfried@uni-vechta.de>2024-09-03 13:01:52 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2024-09-03 13:01:52 +0000
commit9e8c92f415e5106ee9f726b1c7e251924a7769c9 (patch)
tree750e0d1a84f9b4830505d3fdd9992f0e49f14fff /cli/Commands
parentc5fd2ec69e7f181497a0bf4a3d03e3df465cae9a (diff)
add migration-maker, closes #3806
Closes #3806 Merge request studip/studip!3358
Diffstat (limited to 'cli/Commands')
-rw-r--r--cli/Commands/Make/Migration.php108
-rw-r--r--cli/Commands/Make/Model.php125
-rw-r--r--cli/Commands/Make/StudipClassPrinter.php18
3 files changed, 251 insertions, 0 deletions
diff --git a/cli/Commands/Make/Migration.php b/cli/Commands/Make/Migration.php
new file mode 100644
index 0000000..d9ae1d1
--- /dev/null
+++ b/cli/Commands/Make/Migration.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Studip\Cli\Commands\Make;
+
+use Nette\PhpGenerator\PhpFile;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+final class Migration extends Command
+{
+ private const DEFAULT_BRANCH = '0';
+
+ protected static $defaultName = 'make:migration';
+
+ protected function configure(): void
+ {
+ $this->setDescription('Create a new migration file');
+ $this->addArgument('name', InputArgument::REQUIRED, 'The name of the migration');
+ $this->addOption(
+ 'branch',
+ 'b',
+ InputOption::VALUE_OPTIONAL,
+ 'The branch of the migration file',
+ self::DEFAULT_BRANCH
+ );
+ $this->addOption('domain', 'd', InputOption::VALUE_OPTIONAL, 'The domain of the migration file', 'studip');
+
+ $defaultPath = $GLOBALS['STUDIP_BASE_PATH'] . '/db/migrations';
+ $this->addOption(
+ 'path',
+ 'p',
+ InputOption::VALUE_OPTIONAL,
+ 'The location where the migration file should be created',
+ $defaultPath
+ );
+ $this->addOption(
+ 'description',
+ 'D',
+ InputOption::VALUE_OPTIONAL,
+ 'The description for the migration',
+ ''
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $branch = $input->getOption('branch');
+ $domain = $input->getOption('domain');
+ $name = $input->getArgument('name');
+ $path = $input->getOption('path');
+ $description = $input->getOption('description');
+ $verbose = $input->getOption('verbose');
+
+ $version = $this->getNextMigrationVersion($branch, $domain, $path, $verbose);
+ $filename = $this->createMigrationFile($path, $version, $name, $description);
+
+ if ($verbose) {
+ $output->writeln('Migration file ' . $filename . ' created.');
+ }
+
+ return Command::SUCCESS;
+ }
+
+ private function getNextMigrationVersion(string $branch, string $domain, string $path, bool $verbose): string
+ {
+ $version = new \DBSchemaVersion($domain, $branch);
+ $migrator = new \Migrator($path, $version, $verbose);
+ $topVersions = $migrator->topVersion(true);
+
+ if ($branch === self::DEFAULT_BRANCH) {
+ $branches = array_keys($topVersions);
+ usort($branches, 'version_compare');
+ $branch = array_pop($branches);
+ }
+
+ return sprintf('%s.%s', $branch, isset($topVersions[$branch]) ? $topVersions[$branch] + 1 : 1);
+ }
+
+ private function createMigrationFile(string $path, string $version, string $name, string $description): string
+ {
+ if ($description === '') {
+ $description = '// Add content';
+ } else {
+ $description = "return '$description';";
+ }
+ $file = new PhpFile();
+ $class = $file->addClass(str_replace(' ', '', ucwords($name)));
+ $class
+ ->setFinal()
+ ->setExtends(\Migration::class)
+ ->addComment("Description of class.\nSecond line\n");
+ $class->addMethod('description')->addBody($description);
+ $class->addMethod('up')->addBody('// Add content');
+ $class->addMethod('down')->addBody('// Add content');
+
+ $printer = new StudipClassPrinter();
+ $result = $printer->printFile($file);
+ $migrationName = $version . '_' . str_replace(' ', '_', lcfirst($name));
+ $filename = $path . '/' . $migrationName . '.php';
+
+ file_put_contents($filename, $result);
+
+ return $filename;
+ }
+}
diff --git a/cli/Commands/Make/Model.php b/cli/Commands/Make/Model.php
new file mode 100644
index 0000000..57b15af
--- /dev/null
+++ b/cli/Commands/Make/Model.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Studip\Cli\Commands\Make;
+
+use Nette\PhpGenerator\PhpFile;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ChoiceQuestion;
+
+
+final class Model extends Command
+{
+ protected static $defaultName = 'make:model';
+
+ protected function configure(): void
+ {
+ $this->setDescription('Create a sorm-model file');
+ $this->addArgument('name', InputArgument::REQUIRED, 'The name of the sorm-model');
+ $this->addArgument('db-table', InputArgument::OPTIONAL, 'The name of the related db-table');
+ $this->addOption('namespace', 's', InputOption::VALUE_OPTIONAL, 'Namespace', '');
+ $defaultPath = $GLOBALS['STUDIP_BASE_PATH'] . '/lib/models';
+ $this->addOption(
+ 'path',
+ 'p',
+ InputOption::VALUE_OPTIONAL,
+ 'The location where the model file should be created',
+ $defaultPath
+ );
+ }
+
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $namespace = $input->getOption('namespace');
+ $name = $input->getArgument('name');
+ $dbTable = $input->getArgument('db-table');
+ $path = $input->getOption('path');
+ $verbose = $input->getOption('verbose');
+
+ $filename = $this->createModelFile(
+ $path,
+ $name,
+ $input,
+ $output,
+ $dbTable,
+ $namespace
+ );
+
+ if ($verbose) {
+ $output->writeln('Model file ' . $filename . ' created.');
+ }
+
+ return Command::SUCCESS;
+ }
+
+ private function createModelFile(
+ string $path,
+ string $name,
+ InputInterface $input,
+ OutputInterface $output,
+ string $dbTable = null,
+ string $namespace = null,
+ ): string
+ {
+ if (!$dbTable) {
+ $dbTable = strtosnakecase($name);
+ }
+
+ $file = new PhpFile();
+ $className = str_replace(' ', '', ucwords($name));
+
+ if ($namespace) {
+ $className = ucfirst($namespace) . '\\' . $className;
+ }
+ $class = $file->addClass($className);
+ $class->setExtends(\SimpleORMap::class);
+ $class->addComment(ucfirst($name) . '.php');
+ $class->addComment('model class for table ' . $dbTable);
+ $method = $class->addMethod('configure')
+ ->setStatic()
+ ->setProtected();
+
+ $method->addBody(sprintf('$config[\'db_table\'] = \'%s\';', $dbTable));
+ $method->addBody('parent::configure($config);');
+ $method->addParameter('config', []);
+
+
+ $printer = new StudipClassPrinter();
+ $result = $printer->printFile($file);
+
+ $modelName = str_replace(' ', '_', ucfirst($name));
+ $filename = $path . '/' . $modelName . '.php';
+
+ file_put_contents($filename, $result);
+
+ $helper = $this->getHelper('question');
+
+ $tableExists = \DBManager::get()->execute('SHOW TABLES LIKE ?', [$dbTable]);
+
+ $describeModel = false;
+ if ($tableExists) {
+ $question = new ChoiceQuestion(
+ "\nDescribe model:\n",
+ $modelName
+ );
+ $describeModel = $helper->ask($input, $output, $question);
+ }
+
+ if ($describeModel) {
+ $greetInput = new ArrayInput([
+ 'command' => 'sorm:describe',
+ 'name' => 'Fabien',
+ '--yell' => true,
+ ]);
+
+ $returnCode = $this->getApplication()->doRun($greetInput, $output);
+ }
+
+ return $filename;
+ }
+}
diff --git a/cli/Commands/Make/StudipClassPrinter.php b/cli/Commands/Make/StudipClassPrinter.php
new file mode 100644
index 0000000..5e86085
--- /dev/null
+++ b/cli/Commands/Make/StudipClassPrinter.php
@@ -0,0 +1,18 @@
+<?php
+namespace Studip\Cli\Commands\Make;
+
+use Nette\PhpGenerator\Printer;
+
+final class StudipClassPrinter extends Printer
+{
+ /** length of the line after which the line will break */
+ public int $wrapLength = 120;
+ /** indentation character, can be replaced with a sequence of spaces */
+ public string $indentation = ' ';
+ /** number of blank lines between properties */
+ public int $linesBetweenProperties = 0;
+ /** number of blank lines between methods */
+ public int $linesBetweenMethods = 1;
+
+ public string $returnTypeColon = ': ';
+}