From 92b7dd3335a6572debeacfb5faa82c63a5e67888 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Fri, 8 Jun 2018 20:03:25 +0200 Subject: Some minor fixes --- main/app/system/Bakery/Bakery.php | 320 +++--- main/app/system/Bakery/BaseCommand.php | 114 +- main/app/system/Bakery/Command/Bake.php | 150 +-- main/app/system/Bakery/Command/BuildAssets.php | 344 +++--- main/app/system/Bakery/Command/ClearCache.php | 180 ++-- main/app/system/Bakery/Command/Debug.php | 356 +++--- main/app/system/Bakery/Command/Migrate.php | 92 +- main/app/system/Bakery/Command/MigrateRefresh.php | 100 +- main/app/system/Bakery/Command/MigrateReset.php | 94 +- main/app/system/Bakery/Command/MigrateRollback.php | 98 +- main/app/system/Bakery/Command/Setup.php | 440 ++++---- main/app/system/Bakery/Command/Test.php | 108 +- main/app/system/Bakery/DatabaseTest.php | 102 +- main/app/system/Bakery/Migration.php | 134 +-- main/app/system/Bakery/Migrator.php | 1140 ++++++++++---------- 15 files changed, 1886 insertions(+), 1886 deletions(-) (limited to 'main/app/system/Bakery') diff --git a/main/app/system/Bakery/Bakery.php b/main/app/system/Bakery/Bakery.php index 273b25c..99a32b3 100644 --- a/main/app/system/Bakery/Bakery.php +++ b/main/app/system/Bakery/Bakery.php @@ -1,160 +1,160 @@ -setupBaseSprinkleList(); - } - - // Create Symfony Console App - $this->app = new Application("UserFrosting Bakery", \UserFrosting\VERSION); - - // Setup the sprinkles - $uf = new UserFrosting(); - - // Set argument as false, we are using the CLI - $uf->setupSprinkles(FALSE); - - // Get the container - $this->ci = $uf->getContainer(); - - // Add each commands to the Console App - $this->loadCommands(); - } - - /** - * Run the Symfony Console App - */ - public function run() { - $this->app->run(); - } - - /** - * Return the list of available commands for a specific sprinkle - */ - protected function loadCommands() { - // Get base Bakery command - $commands = $this->getBakeryCommands(); - - // Get the sprinkles commands - $sprinkles = $this->ci->sprinkleManager->getSprinkleNames(); - foreach ($sprinkles as $sprinkle) { - $commands = $commands->merge($this->getSprinkleCommands($sprinkle)); - } - - // Add commands to the App - $commands->each(function ($command) { - $instance = new $command(); - $instance->setContainer($this->ci); - $this->app->add($instance); - }); - } - - /** - * Return the list of available commands for a specific sprinkle - * Sprinkles commands should be located in `src/Bakery/` - */ - protected function getSprinkleCommands($sprinkle) { - // Find all the migration files - $path = $this->commandDirectoryPath($sprinkle); - $files = glob($path . "*.php"); - $commands = collect($files); - - // Transform the path into a class names - $commands->transform(function ($file) use ($sprinkle, $path) { - $className = basename($file, '.php'); - $sprinkleName = Str::studly($sprinkle); - $className = "\\UserFrosting\\Sprinkle\\" . $sprinkleName . "\\Bakery\\" . $className; - return $className; - }); - - return $commands; - } - - /** - * Return the list of available commands in system/Bakery/Command/ - */ - protected function getBakeryCommands() { - // Find all the migration files - $files = glob(\UserFrosting\APP_DIR . "/system/Bakery/Command/" . "*.php"); - $commands = collect($files); - - // Transform the path into a class names - $commands->transform(function ($file) { - $className = basename($file, '.php'); - $className = "\\UserFrosting\\System\\Bakery\\Command\\" . $className; - return $className; - }); - - return $commands; - } - - /** - * Returns the path of the Migration directory. - * - * @access protected - * @param mixed $sprinkleName - * @return void - */ - protected function commandDirectoryPath($sprinkleName) { - return \UserFrosting\SPRINKLES_DIR . - \UserFrosting\DS . - $sprinkleName . - \UserFrosting\DS . - \UserFrosting\SRC_DIR_NAME . - "/Bakery/"; - } - - /** - * Write the base Sprinkles schema file if it doesn't exist. - * - * @access protected - * @return void - */ - protected function setupBaseSprinkleList() { - $model = \UserFrosting\APP_DIR . '/sprinkles.example.json'; - $destination = \UserFrosting\SPRINKLES_SCHEMA_FILE; - $sprinklesModelFile = @file_get_contents($model); - if ($sprinklesModelFile === FALSE) { - $this->io->error("File `$sprinklesModelFile` not found. Please create '$destination' manually and try again."); - exit(1); - } - - file_put_contents($destination, $sprinklesModelFile); - - return $sprinklesModelFile; - } -} +setupBaseSprinkleList(); + } + + // Create Symfony Console App + $this->app = new Application("UserFrosting Bakery", \UserFrosting\VERSION); + + // Setup the sprinkles + $uf = new UserFrosting(); + + // Set argument as false, we are using the CLI + $uf->setupSprinkles(FALSE); + + // Get the container + $this->ci = $uf->getContainer(); + + // Add each commands to the Console App + $this->loadCommands(); + } + + /** + * Run the Symfony Console App + */ + public function run() { + $this->app->run(); + } + + /** + * Return the list of available commands for a specific sprinkle + */ + protected function loadCommands() { + // Get base Bakery command + $commands = $this->getBakeryCommands(); + + // Get the sprinkles commands + $sprinkles = $this->ci->sprinkleManager->getSprinkleNames(); + foreach ($sprinkles as $sprinkle) { + $commands = $commands->merge($this->getSprinkleCommands($sprinkle)); + } + + // Add commands to the App + $commands->each(function ($command) { + $instance = new $command(); + $instance->setContainer($this->ci); + $this->app->add($instance); + }); + } + + /** + * Return the list of available commands for a specific sprinkle + * Sprinkles commands should be located in `src/Bakery/` + */ + protected function getSprinkleCommands($sprinkle) { + // Find all the migration files + $path = $this->commandDirectoryPath($sprinkle); + $files = glob($path . "*.php"); + $commands = collect($files); + + // Transform the path into a class names + $commands->transform(function ($file) use ($sprinkle, $path) { + $className = basename($file, '.php'); + $sprinkleName = Str::studly($sprinkle); + $className = "\\UserFrosting\\Sprinkle\\" . $sprinkleName . "\\Bakery\\" . $className; + return $className; + }); + + return $commands; + } + + /** + * Return the list of available commands in system/Bakery/Command/ + */ + protected function getBakeryCommands() { + // Find all the migration files + $files = glob(\UserFrosting\APP_DIR . "/system/Bakery/Command/" . "*.php"); + $commands = collect($files); + + // Transform the path into a class names + $commands->transform(function ($file) { + $className = basename($file, '.php'); + $className = "\\UserFrosting\\System\\Bakery\\Command\\" . $className; + return $className; + }); + + return $commands; + } + + /** + * Returns the path of the Migration directory. + * + * @access protected + * @param mixed $sprinkleName + * @return void + */ + protected function commandDirectoryPath($sprinkleName) { + return \UserFrosting\SPRINKLES_DIR . + \UserFrosting\DS . + $sprinkleName . + \UserFrosting\DS . + \UserFrosting\SRC_DIR_NAME . + "/Bakery/"; + } + + /** + * Write the base Sprinkles schema file if it doesn't exist. + * + * @access protected + * @return void + */ + protected function setupBaseSprinkleList() { + $model = \UserFrosting\APP_DIR . '/sprinkles.example.json'; + $destination = \UserFrosting\SPRINKLES_SCHEMA_FILE; + $sprinklesModelFile = @file_get_contents($model); + if ($sprinklesModelFile === FALSE) { + $this->io->error("File `$sprinklesModelFile` not found. Please create '$destination' manually and try again."); + exit(1); + } + + file_put_contents($destination, $sprinklesModelFile); + + return $sprinklesModelFile; + } +} diff --git a/main/app/system/Bakery/BaseCommand.php b/main/app/system/Bakery/BaseCommand.php index bc1c005..bcefe3d 100644 --- a/main/app/system/Bakery/BaseCommand.php +++ b/main/app/system/Bakery/BaseCommand.php @@ -1,57 +1,57 @@ -io = new SymfonyStyle($input, $output); - $this->projectRoot = \UserFrosting\ROOT_DIR; - } - - /** - * Setup the global container object - */ - public function setContainer(ContainerInterface $ci) { - $this->ci = $ci; - } -} +io = new SymfonyStyle($input, $output); + $this->projectRoot = \UserFrosting\ROOT_DIR; + } + + /** + * Setup the global container object + */ + public function setContainer(ContainerInterface $ci) { + $this->ci = $ci; + } +} diff --git a/main/app/system/Bakery/Command/Bake.php b/main/app/system/Bakery/Command/Bake.php index d12dccd..999171c 100644 --- a/main/app/system/Bakery/Command/Bake.php +++ b/main/app/system/Bakery/Command/Bake.php @@ -1,76 +1,76 @@ -setName("bake") - ->setDescription("UserFrosting installation command") - ->setHelp("This command combine the debug, migrate and build-assets commands."); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->writeln("{$this->title}"); - - $command = $this->getApplication()->find('setup'); - $command->run($input, $output); - - $command = $this->getApplication()->find('debug'); - $command->run($input, $output); - - $command = $this->getApplication()->find('migrate'); - $command->run($input, $output); - - $command = $this->getApplication()->find('create-admin'); - $command->run($input, $output); - - $command = $this->getApplication()->find('build-assets'); - $command->run($input, $output); - - $command = $this->getApplication()->find('clear-cache'); - $command->run($input, $output); - } +setName("bake") + ->setDescription("UserFrosting installation command") + ->setHelp("This command combine the debug, migrate and build-assets commands."); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->writeln("{$this->title}"); + + $command = $this->getApplication()->find('setup'); + $command->run($input, $output); + + $command = $this->getApplication()->find('debug'); + $command->run($input, $output); + + $command = $this->getApplication()->find('migrate'); + $command->run($input, $output); + + $command = $this->getApplication()->find('create-admin'); + $command->run($input, $output); + + $command = $this->getApplication()->find('build-assets'); + $command->run($input, $output); + + $command = $this->getApplication()->find('clear-cache'); + $command->run($input, $output); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/BuildAssets.php b/main/app/system/Bakery/Command/BuildAssets.php index 4a1ab99..d7df634 100644 --- a/main/app/system/Bakery/Command/BuildAssets.php +++ b/main/app/system/Bakery/Command/BuildAssets.php @@ -1,173 +1,173 @@ -setName("build-assets") - ->setDescription("Build the assets using node and npm") - ->setHelp("The build directory contains the scripts and configuration files required to download Javascript, CSS, and other assets used by UserFrosting. This command will install Gulp, Bower, and several other required npm packages locally. With npm set up with all of its required packages, it can be use it to automatically download and install the assets in the correct directories. For more info, see https://learn.userfrosting.com/basics/installation") - ->addOption("compile", "c", InputOption::VALUE_NONE, "Compile the assets and asset bundles for production environment") - ->addOption("force", "f", InputOption::VALUE_NONE, "Force assets compilation by deleting cached data and installed assets before proceeding"); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - // Display header, - $this->io->title("UserFrosting's Assets Builder"); - - // Set $path - $this->buildPath = $this->projectRoot . \UserFrosting\DS . \UserFrosting\BUILD_DIR_NAME; - - // Delete cached data is requested - if ($input->getOption('force')) { - $this->clean(); - } - - // Perform tasks - $this->npmInstall(); - $this->assetsInstall(); - - // Compile if requested - if ($input->getOption('compile') || $this->isProduction()) { - $this->buildAssets(); - } - - // Test the result - $this->checkAssets(); - - // If all went well and there's no fatal errors, we are successful - $this->io->success("Assets install looks successful"); - } - - /** - * Install npm package - * - * @access protected - * @return void - */ - protected function npmInstall() { - $this->io->section("Installing npm dependencies"); - $this->io->writeln("> npm install"); - - // Temporarily change the working directory so we can install npm dependencies - $wd = getcwd(); - chdir($this->buildPath); - passthru("npm install"); - chdir($wd); - } - - /** - * Perform UF Assets installation - * - * @access protected - * @return void - */ - protected function assetsInstall() { - $this->io->section("Installing assets bundles"); - $this->io->writeln("> npm run uf-assets-install"); - passthru("npm run uf-assets-install --prefix " . $this->buildPath); - } - - /** - * Build the production bundle. - * - * @access protected - * @return void - */ - protected function buildAssets() { - $this->io->section("Building assets for production"); - - $this->io->writeln("> npm run uf-bundle-build"); - passthru("npm run uf-bundle-build --prefix " . $this->buildPath); - - $this->io->writeln("> npm run uf-bundle"); - passthru("npm run uf-bundle --prefix " . $this->buildPath); - - $this->io->writeln("> npm run uf-bundle-clean"); - passthru("npm run uf-bundle-clean --prefix " . $this->buildPath); - } - - /** - * Check that the assets where installed in the core sprinkles - * - * @access protected - * @return void - */ - protected function checkAssets() { - $this->io->section("Testing assets installation"); - - // Get path and vendor files - $vendorPath = \UserFrosting\SPRINKLES_DIR . "/core/assets/vendor/*"; - $coreVendorFiles = glob($vendorPath); - - if (!$coreVendorFiles) { - $this->io->error("Assets installation seems to have failed. Directory `$vendorPath` is empty, but it shouldn't be. Check the above log for any errors."); - exit(1); - } - - // Check that `bundle.result.json` is present in production mode - $config = $this->ci->config; - $resultFile = \UserFrosting\ROOT_DIR . \UserFrosting\DS . \UserFrosting\BUILD_DIR_NAME . \UserFrosting\DS . $config['assets.compiled.schema']; - if ($this->isProduction() && !file_exists($resultFile)) { - $this->io->error("Assets building seems to have failed. File `$resultFile` not found. This file is required for production envrionement. Check the above log for any errors."); - exit(1); - } - } - - /** - * Run the `uf-clean` command to delete installed assets, delete compiled - * bundle config file and delete compiled assets - * - * @access protected - * @return void - */ - protected function clean() { - $this->io->section("Cleaning cached data"); - $this->io->writeln("> npm run uf-clean"); - passthru("npm run uf-clean --prefix " . $this->buildPath); - } - - /** - * Return if the app is in production mode - * - * @access protected - * @return bool - */ - protected function isProduction() { - // N.B.: Need to touch the config service first to load dotenv values - $config = $this->ci->config; - $mode = getenv("UF_MODE") ?: ''; - - return ($mode == "production"); - } +setName("build-assets") + ->setDescription("Build the assets using node and npm") + ->setHelp("The build directory contains the scripts and configuration files required to download Javascript, CSS, and other assets used by UserFrosting. This command will install Gulp, Bower, and several other required npm packages locally. With npm set up with all of its required packages, it can be use it to automatically download and install the assets in the correct directories. For more info, see https://learn.userfrosting.com/basics/installation") + ->addOption("compile", "c", InputOption::VALUE_NONE, "Compile the assets and asset bundles for production environment") + ->addOption("force", "f", InputOption::VALUE_NONE, "Force assets compilation by deleting cached data and installed assets before proceeding"); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + // Display header, + $this->io->title("UserFrosting's Assets Builder"); + + // Set $path + $this->buildPath = $this->projectRoot . \UserFrosting\DS . \UserFrosting\BUILD_DIR_NAME; + + // Delete cached data is requested + if ($input->getOption('force')) { + $this->clean(); + } + + // Perform tasks + $this->npmInstall(); + $this->assetsInstall(); + + // Compile if requested + if ($input->getOption('compile') || $this->isProduction()) { + $this->buildAssets(); + } + + // Test the result + $this->checkAssets(); + + // If all went well and there's no fatal errors, we are successful + $this->io->success("Assets install looks successful"); + } + + /** + * Install npm package + * + * @access protected + * @return void + */ + protected function npmInstall() { + $this->io->section("Installing npm dependencies"); + $this->io->writeln("> npm install"); + + // Temporarily change the working directory so we can install npm dependencies + $wd = getcwd(); + chdir($this->buildPath); + passthru("npm install"); + chdir($wd); + } + + /** + * Perform UF Assets installation + * + * @access protected + * @return void + */ + protected function assetsInstall() { + $this->io->section("Installing assets bundles"); + $this->io->writeln("> npm run uf-assets-install"); + passthru("npm run uf-assets-install --prefix " . $this->buildPath); + } + + /** + * Build the production bundle. + * + * @access protected + * @return void + */ + protected function buildAssets() { + $this->io->section("Building assets for production"); + + $this->io->writeln("> npm run uf-bundle-build"); + passthru("npm run uf-bundle-build --prefix " . $this->buildPath); + + $this->io->writeln("> npm run uf-bundle"); + passthru("npm run uf-bundle --prefix " . $this->buildPath); + + $this->io->writeln("> npm run uf-bundle-clean"); + passthru("npm run uf-bundle-clean --prefix " . $this->buildPath); + } + + /** + * Check that the assets where installed in the core sprinkles + * + * @access protected + * @return void + */ + protected function checkAssets() { + $this->io->section("Testing assets installation"); + + // Get path and vendor files + $vendorPath = \UserFrosting\SPRINKLES_DIR . "/core/assets/vendor/*"; + $coreVendorFiles = glob($vendorPath); + + if (!$coreVendorFiles) { + $this->io->error("Assets installation seems to have failed. Directory `$vendorPath` is empty, but it shouldn't be. Check the above log for any errors."); + exit(1); + } + + // Check that `bundle.result.json` is present in production mode + $config = $this->ci->config; + $resultFile = \UserFrosting\ROOT_DIR . \UserFrosting\DS . \UserFrosting\BUILD_DIR_NAME . \UserFrosting\DS . $config['assets.compiled.schema']; + if ($this->isProduction() && !file_exists($resultFile)) { + $this->io->error("Assets building seems to have failed. File `$resultFile` not found. This file is required for production envrionement. Check the above log for any errors."); + exit(1); + } + } + + /** + * Run the `uf-clean` command to delete installed assets, delete compiled + * bundle config file and delete compiled assets + * + * @access protected + * @return void + */ + protected function clean() { + $this->io->section("Cleaning cached data"); + $this->io->writeln("> npm run uf-clean"); + passthru("npm run uf-clean --prefix " . $this->buildPath); + } + + /** + * Return if the app is in production mode + * + * @access protected + * @return bool + */ + protected function isProduction() { + // N.B.: Need to touch the config service first to load dotenv values + $config = $this->ci->config; + $mode = getenv("UF_MODE") ?: ''; + + return ($mode == "production"); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/ClearCache.php b/main/app/system/Bakery/Command/ClearCache.php index 4d30209..ca8e5ed 100644 --- a/main/app/system/Bakery/Command/ClearCache.php +++ b/main/app/system/Bakery/Command/ClearCache.php @@ -1,91 +1,91 @@ -setName("clear-cache") - ->setDescription("Clears the application cache. Includes cache service, Twig and Router cached data"); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->title("Clearing cache"); - - // Clear normal cache - $this->io->writeln(" > Clearing Illuminate cache instance", OutputInterface::VERBOSITY_VERBOSE); - $this->clearIlluminateCache(); - - // Clear Twig cache - $this->io->writeln(" > Clearing Twig cached data", OutputInterface::VERBOSITY_VERBOSE); - if (!$this->clearTwigCache()) { - $this->io->error("Failed to clear Twig cached data. Make sure you have write access to the `app/cache/twig` directory."); - exit(1); - } - - // Clear router cache - $this->io->writeln(" > Clearing Router cache file", OutputInterface::VERBOSITY_VERBOSE); - if (!$this->clearRouterCache()) { - $file = $this->ci->config['settings.routerCacheFile']; - $this->io->error("Failed to delete Router cache file. Make sure you have write access to the `$file` file."); - exit(1); - } - - $this->io->success("Cache cleared !"); - } - - /** - * Flush the cached data from the cache service - * - * @access protected - * @return void - */ - protected function clearIlluminateCache() { - $this->ci->cache->flush(); - } - - /** - * Clear the Twig cache using the Twig CacheHelper class - * - * @access protected - * @return bool true/false if operation is successfull - */ - protected function clearTwigCache() { - $cacheHelper = new CacheHelper($this->ci); - return $cacheHelper->clearCache(); - } - - /** - * Clear the Router cache data file - * - * @access protected - * @return bool true/false if operation is successfull - */ - protected function clearRouterCache() { - return $this->ci->router->clearCache(); - } +setName("clear-cache") + ->setDescription("Clears the application cache. Includes cache service, Twig and Router cached data"); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->title("Clearing cache"); + + // Clear normal cache + $this->io->writeln(" > Clearing Illuminate cache instance", OutputInterface::VERBOSITY_VERBOSE); + $this->clearIlluminateCache(); + + // Clear Twig cache + $this->io->writeln(" > Clearing Twig cached data", OutputInterface::VERBOSITY_VERBOSE); + if (!$this->clearTwigCache()) { + $this->io->error("Failed to clear Twig cached data. Make sure you have write access to the `app/cache/twig` directory."); + exit(1); + } + + // Clear router cache + $this->io->writeln(" > Clearing Router cache file", OutputInterface::VERBOSITY_VERBOSE); + if (!$this->clearRouterCache()) { + $file = $this->ci->config['settings.routerCacheFile']; + $this->io->error("Failed to delete Router cache file. Make sure you have write access to the `$file` file."); + exit(1); + } + + $this->io->success("Cache cleared !"); + } + + /** + * Flush the cached data from the cache service + * + * @access protected + * @return void + */ + protected function clearIlluminateCache() { + $this->ci->cache->flush(); + } + + /** + * Clear the Twig cache using the Twig CacheHelper class + * + * @access protected + * @return bool true/false if operation is successfull + */ + protected function clearTwigCache() { + $cacheHelper = new CacheHelper($this->ci); + return $cacheHelper->clearCache(); + } + + /** + * Clear the Router cache data file + * + * @access protected + * @return bool true/false if operation is successfull + */ + protected function clearRouterCache() { + return $this->ci->router->clearCache(); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/Debug.php b/main/app/system/Bakery/Command/Debug.php index 60565a4..efbf229 100644 --- a/main/app/system/Bakery/Command/Debug.php +++ b/main/app/system/Bakery/Command/Debug.php @@ -1,178 +1,178 @@ -setName("debug") - ->setDescription("Test the UserFrosting installation and setup the database") - ->setHelp("This command is used to check if the various dependencies of UserFrosting are met and display useful debugging information. \nIf any error occurs, check out the online documentation for more info about that error. \nThis command also provide the necessary tools to setup the database credentials"); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - // Display header, - $this->io->title("UserFrosting"); - $this->io->writeln("UserFrosing version : " . \UserFrosting\VERSION); - $this->io->writeln("OS Name : " . php_uname('s')); - $this->io->writeln("Project Root : {$this->projectRoot}"); - - // Need to touch the config service first to load dotenv values - $config = $this->ci->config; - $this->io->writeln("Environment mode : " . getenv("UF_MODE")); - - // Perform tasks - $this->checkPhpVersion(); - $this->checkNodeVersion(); - $this->checkNpmVersion(); - $this->listSprinkles(); - $this->showConfig(); - $this->checkDatabase(); - - // If all went well and there's no fatal errors, we are ready to bake - $this->io->success("Ready to bake !"); - } - - /** - * Check the minimum version of php. - * This is done by composer itself, but we do it again for good mesure - * - * @access public - * @return void - */ - protected function checkPhpVersion() { - $this->io->writeln("PHP Version : " . phpversion()); - if (version_compare(phpversion(), \UserFrosting\PHP_MIN_VERSION, '<')) { - $this->io->error("UserFrosting requires php version " . \UserFrosting\PHP_MIN_VERSION . " or above. You'll need to update you PHP version before you can continue."); - exit(1); - } - } - - /** - * Check the minimum version requirement of Node installed - * - * @access public - * @return void - */ - protected function checkNodeVersion() { - $npmVersion = trim(exec('node -v')); - $this->io->writeln("Node Version : $npmVersion"); - - if (version_compare($npmVersion, 'v4', '<')) { - $this->io->error("UserFrosting requires Node version 4.x or above. Check the documentation for more details."); - exit(1); - } - } - - /** - * Check the minimum version requirement for Npm - * - * @access public - * @return void - */ - protected function checkNpmVersion() { - $npmVersion = trim(exec('npm -v')); - $this->io->writeln("NPM Version : $npmVersion"); - - if (version_compare($npmVersion, '3', '<')) { - $this->io->error("UserFrosting requires npm version 3.x or above. Check the documentation for more details."); - exit(1); - } - } - - /** - * List all sprinkles defined in the Sprinkles schema file, - * making sure this file exist at the same time - * - * @access protected - * @return void - */ - protected function listSprinkles() { - // Check for Sprinkles schema file - $path = \UserFrosting\SPRINKLES_SCHEMA_FILE; - $sprinklesFile = @file_get_contents($path); - if ($sprinklesFile === FALSE) { - $this->io->error("The file `$path` not found."); - } - - // List installed sprinkles - $sprinkles = json_decode($sprinklesFile)->base; - $this->io->section("Loaded sprinkles"); - $this->io->listing($sprinkles); - - // Throw fatal error if the `core` sprinkle is missing - if (!in_array("core", $sprinkles)) { - $this->io->error("The `core` sprinkle is missing from the 'sprinkles.json' file."); - exit(1); - } - } - - /** - * Check the database connexion and setup the `.env` file if we can't - * connect and there's no one found. - * - * @access protected - * @return void - */ - protected function checkDatabase() { - $this->io->section("Testing database connection..."); - - try { - $this->testDB(); - $this->io->writeln("Database connection successful"); - return; - } catch (\Exception $e) { - $error = $e->getMessage(); - $this->io->error($error); - exit(1); - } - } - - /** - * Display database config as for debug purposes - * - * @access protected - * @return void - */ - protected function showConfig() { - // Get config - $config = $this->ci->config; - - // Display database info - $this->io->section("Database config"); - $this->io->writeln([ - "DRIVER : " . $config['db.default.driver'], - "HOST : " . $config['db.default.host'], - "PORT : " . $config['db.default.port'], - "DATABASE : " . $config['db.default.database'], - "USERNAME : " . $config['db.default.username'], - "PASSWORD : " . ($config['db.default.password'] ? "*********" : "") - ]); - } -} +setName("debug") + ->setDescription("Test the UserFrosting installation and setup the database") + ->setHelp("This command is used to check if the various dependencies of UserFrosting are met and display useful debugging information. \nIf any error occurs, check out the online documentation for more info about that error. \nThis command also provide the necessary tools to setup the database credentials"); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + // Display header, + $this->io->title("UserFrosting"); + $this->io->writeln("UserFrosing version : " . \UserFrosting\VERSION); + $this->io->writeln("OS Name : " . php_uname('s')); + $this->io->writeln("Project Root : {$this->projectRoot}"); + + // Need to touch the config service first to load dotenv values + $config = $this->ci->config; + $this->io->writeln("Environment mode : " . getenv("UF_MODE")); + + // Perform tasks + $this->checkPhpVersion(); + $this->checkNodeVersion(); + $this->checkNpmVersion(); + $this->listSprinkles(); + $this->showConfig(); + $this->checkDatabase(); + + // If all went well and there's no fatal errors, we are ready to bake + $this->io->success("Ready to bake !"); + } + + /** + * Check the minimum version of php. + * This is done by composer itself, but we do it again for good mesure + * + * @access public + * @return void + */ + protected function checkPhpVersion() { + $this->io->writeln("PHP Version : " . phpversion()); + if (version_compare(phpversion(), \UserFrosting\PHP_MIN_VERSION, '<')) { + $this->io->error("UserFrosting requires php version " . \UserFrosting\PHP_MIN_VERSION . " or above. You'll need to update you PHP version before you can continue."); + exit(1); + } + } + + /** + * Check the minimum version requirement of Node installed + * + * @access public + * @return void + */ + protected function checkNodeVersion() { + $npmVersion = trim(exec('node -v')); + $this->io->writeln("Node Version : $npmVersion"); + + if (version_compare($npmVersion, 'v4', '<')) { + $this->io->error("UserFrosting requires Node version 4.x or above. Check the documentation for more details."); + exit(1); + } + } + + /** + * Check the minimum version requirement for Npm + * + * @access public + * @return void + */ + protected function checkNpmVersion() { + $npmVersion = trim(exec('npm -v')); + $this->io->writeln("NPM Version : $npmVersion"); + + if (version_compare($npmVersion, '3', '<')) { + $this->io->error("UserFrosting requires npm version 3.x or above. Check the documentation for more details."); + exit(1); + } + } + + /** + * List all sprinkles defined in the Sprinkles schema file, + * making sure this file exist at the same time + * + * @access protected + * @return void + */ + protected function listSprinkles() { + // Check for Sprinkles schema file + $path = \UserFrosting\SPRINKLES_SCHEMA_FILE; + $sprinklesFile = @file_get_contents($path); + if ($sprinklesFile === FALSE) { + $this->io->error("The file `$path` not found."); + } + + // List installed sprinkles + $sprinkles = json_decode($sprinklesFile)->base; + $this->io->section("Loaded sprinkles"); + $this->io->listing($sprinkles); + + // Throw fatal error if the `core` sprinkle is missing + if (!in_array("core", $sprinkles)) { + $this->io->error("The `core` sprinkle is missing from the 'sprinkles.json' file."); + exit(1); + } + } + + /** + * Check the database connexion and setup the `.env` file if we can't + * connect and there's no one found. + * + * @access protected + * @return void + */ + protected function checkDatabase() { + $this->io->section("Testing database connection..."); + + try { + $this->testDB(); + $this->io->writeln("Database connection successful"); + return; + } catch (\Exception $e) { + $error = $e->getMessage(); + $this->io->error($error); + exit(1); + } + } + + /** + * Display database config as for debug purposes + * + * @access protected + * @return void + */ + protected function showConfig() { + // Get config + $config = $this->ci->config; + + // Display database info + $this->io->section("Database config"); + $this->io->writeln([ + "DRIVER : " . $config['db.default.driver'], + "HOST : " . $config['db.default.host'], + "PORT : " . $config['db.default.port'], + "DATABASE : " . $config['db.default.database'], + "USERNAME : " . $config['db.default.username'], + "PASSWORD : " . ($config['db.default.password'] ? "*********" : "") + ]); + } +} diff --git a/main/app/system/Bakery/Command/Migrate.php b/main/app/system/Bakery/Command/Migrate.php index e398628..aad2521 100644 --- a/main/app/system/Bakery/Command/Migrate.php +++ b/main/app/system/Bakery/Command/Migrate.php @@ -1,47 +1,47 @@ -setName("migrate") - ->setDescription("Perform database migration") - ->setHelp("This command runs all the pending database migrations.") - ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->title("UserFrosting's Migrator"); - - $pretend = $input->getOption('pretend'); - - $migrator = new Migrator($this->io, $this->ci); - $migrator->runUp($pretend); - } +setName("migrate") + ->setDescription("Perform database migration") + ->setHelp("This command runs all the pending database migrations.") + ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->title("UserFrosting's Migrator"); + + $pretend = $input->getOption('pretend'); + + $migrator = new Migrator($this->io, $this->ci); + $migrator->runUp($pretend); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/MigrateRefresh.php b/main/app/system/Bakery/Command/MigrateRefresh.php index eb05b61..4c464d0 100644 --- a/main/app/system/Bakery/Command/MigrateRefresh.php +++ b/main/app/system/Bakery/Command/MigrateRefresh.php @@ -1,51 +1,51 @@ -setName("migrate:refresh") - ->setDescription("Rollback the last migration operation and run it up again") - ->addOption('steps', 's', InputOption::VALUE_REQUIRED, 'Number of steps to rollback', 1) - ->addOption('sprinkle', NULL, InputOption::VALUE_REQUIRED, 'The sprinkle to rollback', "") - ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->title("Migration refresh"); - - $step = $input->getOption('steps'); - $sprinkle = $input->getOption('sprinkle'); - $pretend = $input->getOption('pretend'); - - $migrator = new Migrator($this->io, $this->ci); - $migrator->runDown($step, $sprinkle, $pretend); - $migrator->runUp($pretend); - } +setName("migrate:refresh") + ->setDescription("Rollback the last migration operation and run it up again") + ->addOption('steps', 's', InputOption::VALUE_REQUIRED, 'Number of steps to rollback', 1) + ->addOption('sprinkle', NULL, InputOption::VALUE_REQUIRED, 'The sprinkle to rollback', "") + ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->title("Migration refresh"); + + $step = $input->getOption('steps'); + $sprinkle = $input->getOption('sprinkle'); + $pretend = $input->getOption('pretend'); + + $migrator = new Migrator($this->io, $this->ci); + $migrator->runDown($step, $sprinkle, $pretend); + $migrator->runUp($pretend); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/MigrateReset.php b/main/app/system/Bakery/Command/MigrateReset.php index ad66a6c..ceaf5ea 100644 --- a/main/app/system/Bakery/Command/MigrateReset.php +++ b/main/app/system/Bakery/Command/MigrateReset.php @@ -1,48 +1,48 @@ -setName("migrate:reset") - ->setDescription("Reset the whole database to an empty state") - ->addOption('sprinkle', NULL, InputOption::VALUE_REQUIRED, 'The sprinkle to rollback', "") - ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->title("Migration reset"); - - $sprinkle = $input->getOption('sprinkle'); - $pretend = $input->getOption('pretend'); - - $migrator = new Migrator($this->io, $this->ci); - $migrator->runDown(-1, $sprinkle, $pretend); - } +setName("migrate:reset") + ->setDescription("Reset the whole database to an empty state") + ->addOption('sprinkle', NULL, InputOption::VALUE_REQUIRED, 'The sprinkle to rollback', "") + ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->title("Migration reset"); + + $sprinkle = $input->getOption('sprinkle'); + $pretend = $input->getOption('pretend'); + + $migrator = new Migrator($this->io, $this->ci); + $migrator->runDown(-1, $sprinkle, $pretend); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/MigrateRollback.php b/main/app/system/Bakery/Command/MigrateRollback.php index ca2302a..bbc37bb 100644 --- a/main/app/system/Bakery/Command/MigrateRollback.php +++ b/main/app/system/Bakery/Command/MigrateRollback.php @@ -1,50 +1,50 @@ -setName("migrate:rollback") - ->setDescription("Rollback last database migration") - ->addOption('steps', 's', InputOption::VALUE_REQUIRED, 'Number of steps to rollback', 1) - ->addOption('sprinkle', NULL, InputOption::VALUE_REQUIRED, 'The sprinkle to rollback', "") - ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->title("Migration rollback"); - - $step = $input->getOption('steps'); - $sprinkle = $input->getOption('sprinkle'); - $pretend = $input->getOption('pretend'); - - $migrator = new Migrator($this->io, $this->ci); - $migrator->runDown($step, $sprinkle, $pretend); - } +setName("migrate:rollback") + ->setDescription("Rollback last database migration") + ->addOption('steps', 's', InputOption::VALUE_REQUIRED, 'Number of steps to rollback', 1) + ->addOption('sprinkle', NULL, InputOption::VALUE_REQUIRED, 'The sprinkle to rollback', "") + ->addOption('pretend', 'p', InputOption::VALUE_NONE, 'Run migrations in "dry run" mode'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->title("Migration rollback"); + + $step = $input->getOption('steps'); + $sprinkle = $input->getOption('sprinkle'); + $pretend = $input->getOption('pretend'); + + $migrator = new Migrator($this->io, $this->ci); + $migrator->runDown($step, $sprinkle, $pretend); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Command/Setup.php b/main/app/system/Bakery/Command/Setup.php index afea37b..f3f07c8 100644 --- a/main/app/system/Bakery/Command/Setup.php +++ b/main/app/system/Bakery/Command/Setup.php @@ -1,220 +1,220 @@ -setName("setup") - ->setDescription("UserFrosting configuration wizard") - ->setHelp("Helper command to setup the database and email configuration. This can also be done manually by editing the app/.env file or using global server environment variables.") - ->addOption("force", "f", InputOption::VALUE_NONE, "If `.env` file exist, force setup to run again"); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - // Get config - $config = $this->ci->config; - - // Get options - $force = $input->getOption('force'); - - // Display header, - $this->io->title("UserFrosting's Setup Wizard"); - - // Check if the .env file exist. - if (!$force && file_exists($this->envPath)) { - $this->io->note("File `{$this->envPath}` already exist. Use the `php bakery setup -f` command to force setup to run again."); - return; - } - - // There might not be any `.env` file because there may be some custom config or global env values defined on the server. - // We'll check for that. If the configs are empty, we'll assume nothing is defined and go strait to setup. - if (!$force && $config["db.default.host"] != "" && $config["db.default.database"] != "" && $config["db.default.username"] != "") { - $this->io->note("File `{$this->envPath}` was not found, but some database configuration variables are present. Global system environment variables might be defined. If this is not right, use -f option to force setup to run."); - return; - } - - //Goto setup - $this->setupEnv(); - } - - /** - * Setup the `.env` file. - * - * @access public - * @return void - */ - public function setupEnv() { - // Get config - $config = $this->ci->config; - - // Get the db driver choices - $drivers = $this->databaseDrivers(); - - - // Ask the questions - $this->io->section("Setting up database"); - $this->io->note("Database credentials will be saved in `app/.env`"); - - $driver = $this->io->choice("Database type", $drivers->pluck('name')->toArray()); - $driver = $drivers->where('name', $driver)->first(); - - $driverName = $driver['driver']; - $defaultDBName = $driver['defaultDBName']; - - if ($driverName == 'sqlite') { - $name = $this->io->ask("Database name", $defaultDBName); - - $dbParams = [ - 'driver' => $driverName, - 'database' => $name - ]; - } else { - $defaultPort = $driver['defaultPort']; - - $host = $this->io->ask("Hostname", "localhost"); - $port = $this->io->ask("Port", $defaultPort); - $name = $this->io->ask("Database name", $defaultDBName); - $user = $this->io->ask("Username", "userfrosting"); - $password = $this->io->askHidden("Password", function ($password) { - // Use custom validator to accept empty password - return $password; - }); - - $dbParams = [ - 'driver' => $driverName, - 'host' => $host, - 'port' => $port, - 'database' => $name, - 'username' => $user, - 'password' => $password, - 'charset' => $config['db.default.charset'] - ]; - } - - // Setup a new db connection - $capsule = new Capsule; - $capsule->addConnection($dbParams); - - // Test the db connexion. - try { - $conn = $capsule->getConnection(); - $conn->getPdo(); - $this->io->success("Database connection successful"); - $success = TRUE; - } catch (\PDOException $e) { - $message = "Could not connect to the database '{$dbParams['username']}@{$dbParams['host']}/{$dbParams['database']}':" . PHP_EOL; - $message .= "Exception: " . $e->getMessage() . PHP_EOL . PHP_EOL; - $message .= "Please check your database configuration and/or google the exception shown above and run the command again."; - $this->io->error($message); - exit(1); - } - - // Ask for the smtp values now - $this->io->section("Enter your SMTP credentials"); - $this->io->write("This is used to send emails from the system. Edit `app/.env` if you have problems with this later."); - $smtpHost = $this->io->ask("SMTP Host", "host.example.com"); - $smtpUser = $this->io->ask("SMTP User", "relay@example.com"); - $smtpPassword = $this->io->askHidden("SMTP Password", function ($password) { - // Use custom validator to accept empty password - return $password; - }); - - if ($driverName == 'sqlite') { - $fileContent = [ - "UF_MODE=\"\"\n", - "DB_DRIVER=\"{$dbParams['driver']}\"\n", - "DB_NAME=\"{$dbParams['database']}\"\n", - "SMTP_HOST=\"$smtpHost\"\n", - "SMTP_USER=\"$smtpUser\"\n", - "SMTP_PASSWORD=\"$smtpPassword\"\n" - ]; - } else { - $fileContent = [ - "UF_MODE=\"\"\n", - "DB_DRIVER=\"{$dbParams['driver']}\"\n", - "DB_HOST=\"{$dbParams['host']}\"\n", - "DB_PORT=\"{$dbParams['port']}\"\n", - "DB_NAME=\"{$dbParams['database']}\"\n", - "DB_USER=\"{$dbParams['username']}\"\n", - "DB_PASSWORD=\"{$dbParams['password']}\"\n", - "SMTP_HOST=\"$smtpHost\"\n", - "SMTP_USER=\"$smtpUser\"\n", - "SMTP_PASSWORD=\"$smtpPassword\"\n" - ]; - } - - // Let's save this config - file_put_contents($this->envPath, $fileContent); - - // At this point, `$this->uf` is still using the old configs. - // We need to refresh the `db.default` config values - $newConfig = array_merge($config['db.default'], $dbParams); - $this->ci->config->set("db.default", $newConfig); - } - - /** - * Return the database choices for the env setup. - * - * @access protected - * @return void - */ - protected function databaseDrivers() { - return collect([ - [ - "driver" => "mysql", - "name" => "MySQL / MariaDB", - "defaultDBName" => "userfrosting", - "defaultPort" => 3306 - ], - [ - "driver" => "pgsql", - "name" => "ProgreSQL", - "defaultDBName" => "userfrosting", - "defaultPort" => 5432 - ], - [ - "driver" => "sqlsrv", - "name" => "SQL Server", - "defaultDBName" => "userfrosting", - "defaultPort" => 1433 - ], - [ - "driver" => "sqlite", - "name" => "SQLite", - "defaultDBName" => \UserFrosting\DB_DIR . \UserFrosting\DS . 'userfrosting.db', - "defaultPort" => NULL - ] - ]); - } -} +setName("setup") + ->setDescription("UserFrosting configuration wizard") + ->setHelp("Helper command to setup the database and email configuration. This can also be done manually by editing the app/.env file or using global server environment variables.") + ->addOption("force", "f", InputOption::VALUE_NONE, "If `.env` file exist, force setup to run again"); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + // Get config + $config = $this->ci->config; + + // Get options + $force = $input->getOption('force'); + + // Display header, + $this->io->title("UserFrosting's Setup Wizard"); + + // Check if the .env file exist. + if (!$force && file_exists($this->envPath)) { + $this->io->note("File `{$this->envPath}` already exist. Use the `php bakery setup -f` command to force setup to run again."); + return; + } + + // There might not be any `.env` file because there may be some custom config or global env values defined on the server. + // We'll check for that. If the configs are empty, we'll assume nothing is defined and go strait to setup. + if (!$force && $config["db.default.host"] != "" && $config["db.default.database"] != "" && $config["db.default.username"] != "") { + $this->io->note("File `{$this->envPath}` was not found, but some database configuration variables are present. Global system environment variables might be defined. If this is not right, use -f option to force setup to run."); + return; + } + + //Goto setup + $this->setupEnv(); + } + + /** + * Setup the `.env` file. + * + * @access public + * @return void + */ + public function setupEnv() { + // Get config + $config = $this->ci->config; + + // Get the db driver choices + $drivers = $this->databaseDrivers(); + + + // Ask the questions + $this->io->section("Setting up database"); + $this->io->note("Database credentials will be saved in `app/.env`"); + + $driver = $this->io->choice("Database type", $drivers->pluck('name')->toArray()); + $driver = $drivers->where('name', $driver)->first(); + + $driverName = $driver['driver']; + $defaultDBName = $driver['defaultDBName']; + + if ($driverName == 'sqlite') { + $name = $this->io->ask("Database name", $defaultDBName); + + $dbParams = [ + 'driver' => $driverName, + 'database' => $name + ]; + } else { + $defaultPort = $driver['defaultPort']; + + $host = $this->io->ask("Hostname", "localhost"); + $port = $this->io->ask("Port", $defaultPort); + $name = $this->io->ask("Database name", $defaultDBName); + $user = $this->io->ask("Username", "userfrosting"); + $password = $this->io->askHidden("Password", function ($password) { + // Use custom validator to accept empty password + return $password; + }); + + $dbParams = [ + 'driver' => $driverName, + 'host' => $host, + 'port' => $port, + 'database' => $name, + 'username' => $user, + 'password' => $password, + 'charset' => $config['db.default.charset'] + ]; + } + + // Setup a new db connection + $capsule = new Capsule; + $capsule->addConnection($dbParams); + + // Test the db connexion. + try { + $conn = $capsule->getConnection(); + $conn->getPdo(); + $this->io->success("Database connection successful"); + $success = TRUE; + } catch (\PDOException $e) { + $message = "Could not connect to the database '{$dbParams['username']}@{$dbParams['host']}/{$dbParams['database']}':" . PHP_EOL; + $message .= "Exception: " . $e->getMessage() . PHP_EOL . PHP_EOL; + $message .= "Please check your database configuration and/or google the exception shown above and run the command again."; + $this->io->error($message); + exit(1); + } + + // Ask for the smtp values now + $this->io->section("Enter your SMTP credentials"); + $this->io->write("This is used to send emails from the system. Edit `app/.env` if you have problems with this later."); + $smtpHost = $this->io->ask("SMTP Host", "host.example.com"); + $smtpUser = $this->io->ask("SMTP User", "relay@example.com"); + $smtpPassword = $this->io->askHidden("SMTP Password", function ($password) { + // Use custom validator to accept empty password + return $password; + }); + + if ($driverName == 'sqlite') { + $fileContent = [ + "UF_MODE=\"\"\n", + "DB_DRIVER=\"{$dbParams['driver']}\"\n", + "DB_NAME=\"{$dbParams['database']}\"\n", + "SMTP_HOST=\"$smtpHost\"\n", + "SMTP_USER=\"$smtpUser\"\n", + "SMTP_PASSWORD=\"$smtpPassword\"\n" + ]; + } else { + $fileContent = [ + "UF_MODE=\"\"\n", + "DB_DRIVER=\"{$dbParams['driver']}\"\n", + "DB_HOST=\"{$dbParams['host']}\"\n", + "DB_PORT=\"{$dbParams['port']}\"\n", + "DB_NAME=\"{$dbParams['database']}\"\n", + "DB_USER=\"{$dbParams['username']}\"\n", + "DB_PASSWORD=\"{$dbParams['password']}\"\n", + "SMTP_HOST=\"$smtpHost\"\n", + "SMTP_USER=\"$smtpUser\"\n", + "SMTP_PASSWORD=\"$smtpPassword\"\n" + ]; + } + + // Let's save this config + file_put_contents($this->envPath, $fileContent); + + // At this point, `$this->uf` is still using the old configs. + // We need to refresh the `db.default` config values + $newConfig = array_merge($config['db.default'], $dbParams); + $this->ci->config->set("db.default", $newConfig); + } + + /** + * Return the database choices for the env setup. + * + * @access protected + * @return void + */ + protected function databaseDrivers() { + return collect([ + [ + "driver" => "mysql", + "name" => "MySQL / MariaDB", + "defaultDBName" => "userfrosting", + "defaultPort" => 3306 + ], + [ + "driver" => "pgsql", + "name" => "ProgreSQL", + "defaultDBName" => "userfrosting", + "defaultPort" => 5432 + ], + [ + "driver" => "sqlsrv", + "name" => "SQL Server", + "defaultDBName" => "userfrosting", + "defaultPort" => 1433 + ], + [ + "driver" => "sqlite", + "name" => "SQLite", + "defaultDBName" => \UserFrosting\DB_DIR . \UserFrosting\DS . 'userfrosting.db', + "defaultPort" => NULL + ] + ]); + } +} diff --git a/main/app/system/Bakery/Command/Test.php b/main/app/system/Bakery/Command/Test.php index dde187f..9f760b5 100644 --- a/main/app/system/Bakery/Command/Test.php +++ b/main/app/system/Bakery/Command/Test.php @@ -1,55 +1,55 @@ -setName("test") - ->setDescription("Run tests") - ->setHelp("Run php unit tests"); - } - - /** - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $this->io->title("UserFrosting's Tester"); - - // Get command - $command = \UserFrosting\VENDOR_DIR . "/bin/phpunit --colors=always"; - if ($output->isVerbose() || $output->isVeryVerbose()) { - $command .= " -v"; - } - - // Execute - $this->io->writeln("> $command"); - passthru($command); - } +setName("test") + ->setDescription("Run tests") + ->setHelp("Run php unit tests"); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->io->title("UserFrosting's Tester"); + + // Get command + $command = \UserFrosting\VENDOR_DIR . "/bin/phpunit --colors=always"; + if ($output->isVerbose() || $output->isVeryVerbose()) { + $command .= " -v"; + } + + // Execute + $this->io->writeln("> $command"); + passthru($command); + } } \ No newline at end of file diff --git a/main/app/system/Bakery/DatabaseTest.php b/main/app/system/Bakery/DatabaseTest.php index 91d2c2b..046c4c5 100644 --- a/main/app/system/Bakery/DatabaseTest.php +++ b/main/app/system/Bakery/DatabaseTest.php @@ -1,52 +1,52 @@ -ci->db; - - // Get config - $config = $this->ci->config; - - // Check params are valids - $dbParams = $config['db.default']; - if (!$dbParams) { - throw new \Exception("'default' database connection not found. Please double-check your configuration."); - } - - // Test database connection directly using PDO - try { - Capsule::connection()->getPdo(); - } catch (\PDOException $e) { - $message = "Could not connect to the database '{$dbParams['username']}@{$dbParams['host']}/{$dbParams['database']}':" . PHP_EOL; - $message .= "Exception: " . $e->getMessage() . PHP_EOL . PHP_EOL; - $message .= "Please check your database configuration and/or google the exception shown above and run command again."; - throw new \Exception($message); - } - - return TRUE; - } +ci->db; + + // Get config + $config = $this->ci->config; + + // Check params are valids + $dbParams = $config['db.default']; + if (!$dbParams) { + throw new \Exception("'default' database connection not found. Please double-check your configuration."); + } + + // Test database connection directly using PDO + try { + Capsule::connection()->getPdo(); + } catch (\PDOException $e) { + $message = "Could not connect to the database '{$dbParams['username']}@{$dbParams['host']}/{$dbParams['database']}':" . PHP_EOL; + $message .= "Exception: " . $e->getMessage() . PHP_EOL . PHP_EOL; + $message .= "Please check your database configuration and/or google the exception shown above and run command again."; + throw new \Exception($message); + } + + return TRUE; + } } \ No newline at end of file diff --git a/main/app/system/Bakery/Migration.php b/main/app/system/Bakery/Migration.php index 7c4ef62..c4255d9 100644 --- a/main/app/system/Bakery/Migration.php +++ b/main/app/system/Bakery/Migration.php @@ -1,67 +1,67 @@ -schema = $schema; - $this->io = $io; - } - - /** - * Method to apply changes to the database - */ - public function up() { - } - - /** - * Method to revert changes applied by the `up` method - */ - public function down() { - } - - /** - * Method to seed new information to the database - */ - public function seed() { - } -} +schema = $schema; + $this->io = $io; + } + + /** + * Method to apply changes to the database + */ + public function up() { + } + + /** + * Method to revert changes applied by the `up` method + */ + public function down() { + } + + /** + * Method to seed new information to the database + */ + public function seed() { + } +} diff --git a/main/app/system/Bakery/Migrator.php b/main/app/system/Bakery/Migrator.php index ff00b0b..568febd 100644 --- a/main/app/system/Bakery/Migrator.php +++ b/main/app/system/Bakery/Migrator.php @@ -1,570 +1,570 @@ -io = $io; - $this->ci = $ci; - - // Start by testing the DB connexion, just in case - try { - $this->io->writeln("Testing database connection", OutputInterface::VERBOSITY_VERBOSE); - $this->testDB(); - $this->io->writeln("Ok", OutputInterface::VERBOSITY_VERBOSE); - } catch (\Exception $e) { - $this->io->error($e->getMessage()); - exit(1); - } - - // Get schema required to run the table blueprints - $this->schema = Capsule::schema(); - - // Make sure the setup table exist - $this->setupVersionTable(); - } - - /** - * Run all the migrations available - * - * @access public - * @param bool $pretend (default: false) - * @return void - */ - public function runUp($pretend = FALSE) { - // Get installed migrations and pluck by class name. We only need this for now - $migrations = Migrations::get(); - $this->installed = $migrations->pluck('migration'); - - $this->io->writeln("\nInstalled migrations:", OutputInterface::VERBOSITY_VERBOSE); - $this->io->writeln($this->installed->toArray(), OutputInterface::VERBOSITY_VERBOSE); - - // Get pending migrations - $this->io->section("Fetching available migrations"); - $this->pending = $this->getPendingMigrations(); - - // If there's no pending migration, don't need to go further - if ($this->pending->isEmpty()) { - $this->io->success("Nothing to migrate !"); - return; - } - - // Resolve the dependencies - $this->resolveDependencies(); - - // If there are any unfulfillable migration, we can't continue - if (!$this->unfulfillable->isEmpty()) { - - $msg = "\nSome migrations dependencies can't be met. Check those migrations for unmet dependencies and try again:"; - - foreach ($this->unfulfillable as $migration) { - $msg .= "\n{$migration->className} depends on \n - "; - $msg .= implode("\n - ", $migration->dependencies); - $msg .= "\n"; - } - - $this->io->error($msg); - exit(1); - } - - // Ready to run ! - $this->io->section("Running migrations"); - - if ($pretend) { - $this->io->note("Running migration in pretend mode"); - } - - // We have a list of fulfillable migration, we run them up! - foreach ($this->fulfillable as $migration) { - $this->io->write("\n> Migrating {$migration->className}..."); - - if ($pretend) { - $this->io->newLine(); - $this->pretendToRun($migration, 'up'); - } else { - $migration->up(); - $migration->seed(); - $this->log($migration); - $this->io->writeln(" Done!"); - } - } - - // If all went well and there's no fatal errors, we are ready to bake - $this->io->success("Migration successful !"); - } - - /** - * Rollback the last btach of migrations. - * - * @access public - * @param int $step (default: 1). Number of batch we will be going back. -1 revert all migrations - * @param string $sprinkle (default: "") Limit rollback to a specific sprinkle - * @param bool $pretend (default: false) - * @return void - */ - public function runDown($step = 1, $sprinkle = "", $pretend = FALSE) { - // Can't go furhter down than 1 step - if ($step <= 0 && $step != -1) { - throw new \InvalidArgumentException("Step can't be 0 or less"); - } - - // Get last batch number - $batch = $this->getNextBatchNumber(); - - // Calculate the number of steps back we need to take - if ($step == -1) { - $stepsBack = 1; - $this->io->warning("Rolling back all migrations"); - } else { - $stepsBack = max($batch - $step, 1); - $this->io->note("Rolling back $step steps to batch $stepsBack", OutputInterface::VERBOSITY_VERBOSE); - } - - // Get installed migrations - $migrations = Migrations::orderBy("id", "desc")->where('batch', '>=', $stepsBack); - - // Add the sprinkle requirement too - if ($sprinkle != "") { - $this->io->note("Rolling back sprinkle `$sprinkle`", OutputInterface::VERBOSITY_VERBOSE); - $migrations->where('sprinkle', $sprinkle); - } - - // Run query - $migrations = $migrations->get(); - - // If there's nothing to rollback, stop here - if ($migrations->isEmpty()) { - $this->io->writeln("Nothing to rollback"); - exit(1); - } - - // Get pending migrations - $this->io->writeln("Migration to rollback:"); - $this->io->listing($migrations->pluck('migration')->toArray()); - - // Ask confirmation to continue. - if (!$pretend && !$this->io->confirm("Continue?", FALSE)) { - exit(1); - } - - if ($pretend) { - $this->io->note("Rolling back in pretend mode"); - } - - // Loop again to run down each migration - foreach ($migrations as $migration) { - - // Check if those migration class are available - if (!class_exists($migration->migration)) { - $this->io->warning("Migration class {$migration->migration} doesn't exist."); - continue; - } - - $this->io->write("> Rolling back {$migration->migration}..."); - $migrationClass = $migration->migration; - $instance = new $migrationClass($this->schema, $this->io); - - if ($pretend) { - $this->io->newLine(); - $this->pretendToRun($instance, 'down'); - } else { - $instance->down(); - $migration->delete(); - $this->io->writeln(" Done!"); - } - - $this->io->newLine(); - } - - // If all went well and there's no fatal errors, we are ready to bake - $this->io->success("Rollback successful !"); - } - - /** - * Pretend to run migration class. - * - * @access protected - * @param mixed $migration - * @param string $method up/down - */ - protected function pretendToRun($migration, $method) { - foreach ($this->getQueries($migration, $method) as $query) { - $this->io->writeln($query['query'], OutputInterface::VERBOSITY_VERBOSE); - } - } - - /** - * Return all of the queries that would be run for a migration. - * - * @access protected - * @param mixed $migration - * @param string $method up/down - * @return void - */ - protected function getQueries($migration, $method) { - $db = $this->schema->getConnection(); - - return $db->pretend(function () use ($migration, $method) { - if (method_exists($migration, $method)) { - $migration->{$method}(); - } - }); - } - - /** - * Get pending migrations by looking at all the migration files - * and finding the one not yet runed by compairing with the ran migrations - * - * @access protected - * @return void - */ - protected function getPendingMigrations() { - $pending = collect([]); - - // Get the sprinkle list - $sprinkles = $this->ci->sprinkleManager->getSprinkleNames(); - - // Loop all the sprinkles to find their pending migrations - foreach ($sprinkles as $sprinkle) { - - $this->io->writeln("> Fetching from `$sprinkle`"); - - // We get all the migrations. This will return them as a colleciton of class names - $migrations = $this->getMigrations($sprinkle); - - // We filter the available migration by removing the one that have already been run - // This reject the class name found in the installed collection - $migrations = $migrations->reject(function ($value, $key) { - return $this->installed->contains($value); - }); - - // Load each class - foreach ($migrations as $migrationClass) { - - // Make sure the class exist - if (!class_exists($migrationClass)) { - throw new BadClassNameException("Unable to find the migration class '$migrationClass'."); - } - - // Load the migration class - $migration = new $migrationClass($this->schema, $this->io); - - //Set the sprinkle - $migration->sprinkle = $sprinkle; - - // Also set the class name. We could find it using ::class, but this - // will make it easier to manipulate the collection - $migration->className = $migrationClass; - - // Add it to the pending list - $pending->push($migration); - } - } - - // Get pending migrations - $pendingArray = ($pending->pluck('className')->toArray()) ?: ""; - $this->io->writeln("\nPending migrations:", OutputInterface::VERBOSITY_VERBOSE); - $this->io->writeln($pendingArray, OutputInterface::VERBOSITY_VERBOSE); - - return $pending; - } - - /** - * Get the list of migrations avaiables in the filesystem. - * Return a list of resolved className - * - * @access public - * @param string $sprinkleName - * @return void - */ - public function getMigrations($sprinkle) { - // Find all the migration files - $path = $this->migrationDirectoryPath($sprinkle); - $files = glob($path . "*/*.php"); - - // Transform the array in a collection - $migrations = collect($files); - - // We transform the path into a migration object - $migrations->transform(function ($file) use ($sprinkle, $path) { - // Deconstruct the path - $migration = str_replace($path, "", $file); - $className = basename($file, '.php'); - $sprinkleName = Str::studly($sprinkle); - $version = str_replace("/$className.php", "", $migration); - - // Reconstruct the classname - $className = "\\UserFrosting\\Sprinkle\\" . $sprinkleName . "\\Database\\Migrations\\" . $version . "\\" . $className; - - return $className; - }); - - return $migrations; - } - - /** - * Resolve all the dependencies for all the pending migrations - * This function fills in the `fullfillable` and `unfulfillable` list - * - * @access protected - * @return void - */ - protected function resolveDependencies() { - $this->io->writeln("\nResolving migrations dependencies...", OutputInterface::VERBOSITY_VERBOSE); - - // Reset fulfillable/unfulfillable lists - $this->fulfillable = collect([]); - $this->unfulfillable = collect([]); - - // Loop pending and check for dependencies - foreach ($this->pending as $migration) { - $this->validateClassDependencies($migration); - } - - $fulfillable = ($this->fulfillable->pluck('className')->toArray()) ?: ""; - $this->io->writeln("\nFulfillable migrations:", OutputInterface::VERBOSITY_VERBOSE); - $this->io->writeln($fulfillable, OutputInterface::VERBOSITY_VERBOSE); - - $unfulfillable = ($this->unfulfillable->pluck('className')->toArray()) ?: ""; - $this->io->writeln("\nUnfulfillable migrations:", OutputInterface::VERBOSITY_VERBOSE); - $this->io->writeln($unfulfillable, OutputInterface::VERBOSITY_VERBOSE); - } - - /** - * Check if a migration dependencies are met. - * To test if a migration is fulfillable, the class must : - * Already been installed OR exist and have all it's dependencies met - * - * @access protected - * @param $migration - * @return bool true/false if all conditions are met - */ - protected function validateClassDependencies($migration) { - $this->io->writeln("> Checking dependencies for {$migration->className}", OutputInterface::VERBOSITY_VERBOSE); - - // If it's already marked as fulfillable, it's fulfillable - // Return true directly (it's already marked) - if ($this->fulfillable->contains($migration)) { - return TRUE; - } - - // If it's already marked as unfulfillable, it's unfulfillable - // Return false directly (it's already marked) - if ($this->unfulfillable->contains($migration)) { - return FALSE; - } - - // If it's already run, it's fulfillable - // Mark it as such for next time it comes up in this loop - if ($this->installed->contains($migration->className)) { - return $this->markAsFulfillable($migration); - } - - // Loop dependencies. If one is not fulfillable, then this one is not either - foreach ($migration->dependencies as $dependencyClass) { - - // The dependency might already be installed. Check that first - if ($this->installed->contains($dependencyClass)) { - continue; - } - - // Try to find it in the `pending` list. Cant' find it? Then it's not fulfillable - $dependency = $this->pending->where('className', $dependencyClass)->first(); - - // Check migration dependencies of this one right now - // If ti's not fullfillable, then this one isn't either - if (!$dependency || !$this->validateClassDependencies($dependency)) { - return $this->markAsUnfulfillable($migration); - } - } - - // If no dependencies returned false, it's fulfillable - return $this->markAsFulfillable($migration); - } - - /** - * Mark a dependency as fulfillable. - * Removes it from the pending list and add it to the fulfillable list - * - * @access protected - * @param $migration - * @return true - */ - protected function markAsFulfillable($migration) { - $this->fulfillable->push($migration); - return TRUE; - } - - /** - * Mark a dependency as unfulfillable. - * Removes it from the pending list and add it to the unfulfillable list - * - * @access protected - * @param $migration - * @return false - */ - protected function markAsUnfulfillable($migration) { - $this->unfulfillable->push($migration); - return FALSE; - } - - /** - * Log that a migration was run. - * - * @access public - * @param mixed $migration - * @return void - */ - protected function log($migration) { - // Get the next batch number if not defined - if (!$this->batch) { - $this->batch = $this->getNextBatchNumber(); - } - - $log = new Migrations([ - 'sprinkle' => $migration->sprinkle, - 'migration' => $migration->className, - 'batch' => $this->batch - ]); - $log->save(); - } - - /** - * Return the next batch number from the db. - * Batch number is used to group together migration run in the same operation - * - * @access public - * @return int the next batch number - */ - public function getNextBatchNumber() { - $batch = Migrations::max('batch'); - return ($batch) ? $batch + 1 : 1; - } - - /** - * Create the migration history table if needed. - * Also check if the tables requires migrations - * We run the migration file manually for this one - * - * @access public - * @return void - */ - protected function setupVersionTable() { - // Check if the `migrations` table exist. Create it manually otherwise - if (!$this->schema->hasColumn($this->table, 'id')) { - $this->io->section("Creating the `{$this->table}` table"); - - $migration = new \UserFrosting\System\Database\Migrations\v410\MigrationTable($this->schema, $this->io); - $migration->up(); - - $this->io->success("Table `{$this->table}` created"); - } - } - - /** - * Returns the path of the Migration directory. - * - * @access protected - * @param mixed $sprinkleName - * @return void - */ - protected function migrationDirectoryPath($sprinkleName) { - $path = \UserFrosting\SPRINKLES_DIR . - \UserFrosting\DS . - $sprinkleName . - \UserFrosting\DS . - \UserFrosting\SRC_DIR_NAME . - "/Database/Migrations/"; - - return $path; - } -} +io = $io; + $this->ci = $ci; + + // Start by testing the DB connexion, just in case + try { + $this->io->writeln("Testing database connection", OutputInterface::VERBOSITY_VERBOSE); + $this->testDB(); + $this->io->writeln("Ok", OutputInterface::VERBOSITY_VERBOSE); + } catch (\Exception $e) { + $this->io->error($e->getMessage()); + exit(1); + } + + // Get schema required to run the table blueprints + $this->schema = Capsule::schema(); + + // Make sure the setup table exist + $this->setupVersionTable(); + } + + /** + * Run all the migrations available + * + * @access public + * @param bool $pretend (default: false) + * @return void + */ + public function runUp($pretend = FALSE) { + // Get installed migrations and pluck by class name. We only need this for now + $migrations = Migrations::get(); + $this->installed = $migrations->pluck('migration'); + + $this->io->writeln("\nInstalled migrations:", OutputInterface::VERBOSITY_VERBOSE); + $this->io->writeln($this->installed->toArray(), OutputInterface::VERBOSITY_VERBOSE); + + // Get pending migrations + $this->io->section("Fetching available migrations"); + $this->pending = $this->getPendingMigrations(); + + // If there's no pending migration, don't need to go further + if ($this->pending->isEmpty()) { + $this->io->success("Nothing to migrate !"); + return; + } + + // Resolve the dependencies + $this->resolveDependencies(); + + // If there are any unfulfillable migration, we can't continue + if (!$this->unfulfillable->isEmpty()) { + + $msg = "\nSome migrations dependencies can't be met. Check those migrations for unmet dependencies and try again:"; + + foreach ($this->unfulfillable as $migration) { + $msg .= "\n{$migration->className} depends on \n - "; + $msg .= implode("\n - ", $migration->dependencies); + $msg .= "\n"; + } + + $this->io->error($msg); + exit(1); + } + + // Ready to run ! + $this->io->section("Running migrations"); + + if ($pretend) { + $this->io->note("Running migration in pretend mode"); + } + + // We have a list of fulfillable migration, we run them up! + foreach ($this->fulfillable as $migration) { + $this->io->write("\n> Migrating {$migration->className}..."); + + if ($pretend) { + $this->io->newLine(); + $this->pretendToRun($migration, 'up'); + } else { + $migration->up(); + $migration->seed(); + $this->log($migration); + $this->io->writeln(" Done!"); + } + } + + // If all went well and there's no fatal errors, we are ready to bake + $this->io->success("Migration successful !"); + } + + /** + * Rollback the last btach of migrations. + * + * @access public + * @param int $step (default: 1). Number of batch we will be going back. -1 revert all migrations + * @param string $sprinkle (default: "") Limit rollback to a specific sprinkle + * @param bool $pretend (default: false) + * @return void + */ + public function runDown($step = 1, $sprinkle = "", $pretend = FALSE) { + // Can't go furhter down than 1 step + if ($step <= 0 && $step != -1) { + throw new \InvalidArgumentException("Step can't be 0 or less"); + } + + // Get last batch number + $batch = $this->getNextBatchNumber(); + + // Calculate the number of steps back we need to take + if ($step == -1) { + $stepsBack = 1; + $this->io->warning("Rolling back all migrations"); + } else { + $stepsBack = max($batch - $step, 1); + $this->io->note("Rolling back $step steps to batch $stepsBack", OutputInterface::VERBOSITY_VERBOSE); + } + + // Get installed migrations + $migrations = Migrations::orderBy("id", "desc")->where('batch', '>=', $stepsBack); + + // Add the sprinkle requirement too + if ($sprinkle != "") { + $this->io->note("Rolling back sprinkle `$sprinkle`", OutputInterface::VERBOSITY_VERBOSE); + $migrations->where('sprinkle', $sprinkle); + } + + // Run query + $migrations = $migrations->get(); + + // If there's nothing to rollback, stop here + if ($migrations->isEmpty()) { + $this->io->writeln("Nothing to rollback"); + exit(1); + } + + // Get pending migrations + $this->io->writeln("Migration to rollback:"); + $this->io->listing($migrations->pluck('migration')->toArray()); + + // Ask confirmation to continue. + if (!$pretend && !$this->io->confirm("Continue?", FALSE)) { + exit(1); + } + + if ($pretend) { + $this->io->note("Rolling back in pretend mode"); + } + + // Loop again to run down each migration + foreach ($migrations as $migration) { + + // Check if those migration class are available + if (!class_exists($migration->migration)) { + $this->io->warning("Migration class {$migration->migration} doesn't exist."); + continue; + } + + $this->io->write("> Rolling back {$migration->migration}..."); + $migrationClass = $migration->migration; + $instance = new $migrationClass($this->schema, $this->io); + + if ($pretend) { + $this->io->newLine(); + $this->pretendToRun($instance, 'down'); + } else { + $instance->down(); + $migration->delete(); + $this->io->writeln(" Done!"); + } + + $this->io->newLine(); + } + + // If all went well and there's no fatal errors, we are ready to bake + $this->io->success("Rollback successful !"); + } + + /** + * Pretend to run migration class. + * + * @access protected + * @param mixed $migration + * @param string $method up/down + */ + protected function pretendToRun($migration, $method) { + foreach ($this->getQueries($migration, $method) as $query) { + $this->io->writeln($query['query'], OutputInterface::VERBOSITY_VERBOSE); + } + } + + /** + * Return all of the queries that would be run for a migration. + * + * @access protected + * @param mixed $migration + * @param string $method up/down + * @return void + */ + protected function getQueries($migration, $method) { + $db = $this->schema->getConnection(); + + return $db->pretend(function () use ($migration, $method) { + if (method_exists($migration, $method)) { + $migration->{$method}(); + } + }); + } + + /** + * Get pending migrations by looking at all the migration files + * and finding the one not yet runed by compairing with the ran migrations + * + * @access protected + * @return void + */ + protected function getPendingMigrations() { + $pending = collect([]); + + // Get the sprinkle list + $sprinkles = $this->ci->sprinkleManager->getSprinkleNames(); + + // Loop all the sprinkles to find their pending migrations + foreach ($sprinkles as $sprinkle) { + + $this->io->writeln("> Fetching from `$sprinkle`"); + + // We get all the migrations. This will return them as a colleciton of class names + $migrations = $this->getMigrations($sprinkle); + + // We filter the available migration by removing the one that have already been run + // This reject the class name found in the installed collection + $migrations = $migrations->reject(function ($value, $key) { + return $this->installed->contains($value); + }); + + // Load each class + foreach ($migrations as $migrationClass) { + + // Make sure the class exist + if (!class_exists($migrationClass)) { + throw new BadClassNameException("Unable to find the migration class '$migrationClass'."); + } + + // Load the migration class + $migration = new $migrationClass($this->schema, $this->io); + + //Set the sprinkle + $migration->sprinkle = $sprinkle; + + // Also set the class name. We could find it using ::class, but this + // will make it easier to manipulate the collection + $migration->className = $migrationClass; + + // Add it to the pending list + $pending->push($migration); + } + } + + // Get pending migrations + $pendingArray = ($pending->pluck('className')->toArray()) ?: ""; + $this->io->writeln("\nPending migrations:", OutputInterface::VERBOSITY_VERBOSE); + $this->io->writeln($pendingArray, OutputInterface::VERBOSITY_VERBOSE); + + return $pending; + } + + /** + * Get the list of migrations avaiables in the filesystem. + * Return a list of resolved className + * + * @access public + * @param string $sprinkleName + * @return void + */ + public function getMigrations($sprinkle) { + // Find all the migration files + $path = $this->migrationDirectoryPath($sprinkle); + $files = glob($path . "*/*.php"); + + // Transform the array in a collection + $migrations = collect($files); + + // We transform the path into a migration object + $migrations->transform(function ($file) use ($sprinkle, $path) { + // Deconstruct the path + $migration = str_replace($path, "", $file); + $className = basename($file, '.php'); + $sprinkleName = Str::studly($sprinkle); + $version = str_replace("/$className.php", "", $migration); + + // Reconstruct the classname + $className = "\\UserFrosting\\Sprinkle\\" . $sprinkleName . "\\Database\\Migrations\\" . $version . "\\" . $className; + + return $className; + }); + + return $migrations; + } + + /** + * Resolve all the dependencies for all the pending migrations + * This function fills in the `fullfillable` and `unfulfillable` list + * + * @access protected + * @return void + */ + protected function resolveDependencies() { + $this->io->writeln("\nResolving migrations dependencies...", OutputInterface::VERBOSITY_VERBOSE); + + // Reset fulfillable/unfulfillable lists + $this->fulfillable = collect([]); + $this->unfulfillable = collect([]); + + // Loop pending and check for dependencies + foreach ($this->pending as $migration) { + $this->validateClassDependencies($migration); + } + + $fulfillable = ($this->fulfillable->pluck('className')->toArray()) ?: ""; + $this->io->writeln("\nFulfillable migrations:", OutputInterface::VERBOSITY_VERBOSE); + $this->io->writeln($fulfillable, OutputInterface::VERBOSITY_VERBOSE); + + $unfulfillable = ($this->unfulfillable->pluck('className')->toArray()) ?: ""; + $this->io->writeln("\nUnfulfillable migrations:", OutputInterface::VERBOSITY_VERBOSE); + $this->io->writeln($unfulfillable, OutputInterface::VERBOSITY_VERBOSE); + } + + /** + * Check if a migration dependencies are met. + * To test if a migration is fulfillable, the class must : + * Already been installed OR exist and have all it's dependencies met + * + * @access protected + * @param $migration + * @return bool true/false if all conditions are met + */ + protected function validateClassDependencies($migration) { + $this->io->writeln("> Checking dependencies for {$migration->className}", OutputInterface::VERBOSITY_VERBOSE); + + // If it's already marked as fulfillable, it's fulfillable + // Return true directly (it's already marked) + if ($this->fulfillable->contains($migration)) { + return TRUE; + } + + // If it's already marked as unfulfillable, it's unfulfillable + // Return false directly (it's already marked) + if ($this->unfulfillable->contains($migration)) { + return FALSE; + } + + // If it's already run, it's fulfillable + // Mark it as such for next time it comes up in this loop + if ($this->installed->contains($migration->className)) { + return $this->markAsFulfillable($migration); + } + + // Loop dependencies. If one is not fulfillable, then this one is not either + foreach ($migration->dependencies as $dependencyClass) { + + // The dependency might already be installed. Check that first + if ($this->installed->contains($dependencyClass)) { + continue; + } + + // Try to find it in the `pending` list. Cant' find it? Then it's not fulfillable + $dependency = $this->pending->where('className', $dependencyClass)->first(); + + // Check migration dependencies of this one right now + // If ti's not fullfillable, then this one isn't either + if (!$dependency || !$this->validateClassDependencies($dependency)) { + return $this->markAsUnfulfillable($migration); + } + } + + // If no dependencies returned false, it's fulfillable + return $this->markAsFulfillable($migration); + } + + /** + * Mark a dependency as fulfillable. + * Removes it from the pending list and add it to the fulfillable list + * + * @access protected + * @param $migration + * @return true + */ + protected function markAsFulfillable($migration) { + $this->fulfillable->push($migration); + return TRUE; + } + + /** + * Mark a dependency as unfulfillable. + * Removes it from the pending list and add it to the unfulfillable list + * + * @access protected + * @param $migration + * @return false + */ + protected function markAsUnfulfillable($migration) { + $this->unfulfillable->push($migration); + return FALSE; + } + + /** + * Log that a migration was run. + * + * @access public + * @param mixed $migration + * @return void + */ + protected function log($migration) { + // Get the next batch number if not defined + if (!$this->batch) { + $this->batch = $this->getNextBatchNumber(); + } + + $log = new Migrations([ + 'sprinkle' => $migration->sprinkle, + 'migration' => $migration->className, + 'batch' => $this->batch + ]); + $log->save(); + } + + /** + * Return the next batch number from the db. + * Batch number is used to group together migration run in the same operation + * + * @access public + * @return int the next batch number + */ + public function getNextBatchNumber() { + $batch = Migrations::max('batch'); + return ($batch) ? $batch + 1 : 1; + } + + /** + * Create the migration history table if needed. + * Also check if the tables requires migrations + * We run the migration file manually for this one + * + * @access public + * @return void + */ + protected function setupVersionTable() { + // Check if the `migrations` table exist. Create it manually otherwise + if (!$this->schema->hasColumn($this->table, 'id')) { + $this->io->section("Creating the `{$this->table}` table"); + + $migration = new \UserFrosting\System\Database\Migrations\v410\MigrationTable($this->schema, $this->io); + $migration->up(); + + $this->io->success("Table `{$this->table}` created"); + } + } + + /** + * Returns the path of the Migration directory. + * + * @access protected + * @param mixed $sprinkleName + * @return void + */ + protected function migrationDirectoryPath($sprinkleName) { + $path = \UserFrosting\SPRINKLES_DIR . + \UserFrosting\DS . + $sprinkleName . + \UserFrosting\DS . + \UserFrosting\SRC_DIR_NAME . + "/Database/Migrations/"; + + return $path; + } +} -- cgit v1.2.3