delagics/yii2-app-another

View on GitHub
app/console/controllers/InitController.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace console\controllers;

use Yii;
use Exception;
use yii\di\Instance;
use yii\db\Connection;
use yii\helpers\ArrayHelper;
use console\helpers\Console;
use console\helpers\Initializer;
use console\components\Controller;

/**
 * Project initialization commands - provides a list of commands for fast project initialization.
 *
 * @package console\controllers
 * @property Connection $db
 */
class InitController extends Controller
{
    /**
     * @var Connection|array|string the DB connection object or the application component ID of the DB connection to use
     * when applying migrations. Starting from version 2.0.3, this can also be a configuration array
     * for creating the object.
     */
    public $db = 'db';

    /**
     * @var int color used for formatting notable messages.
     */
    public $colorNotable = Console::FG_YELLOW;
    /**
     * @var int color used for formatting success messages.
     */
    public $colorSuccess = Console::FG_GREEN;
    /**
     * @var int color used for formatting error messages.
     */
    public $colorError = Console::FG_RED;
    /**
     * @var int color used for formatting standard messages.
     */
    public $colorDefault = Console::FG_GREY;

    /**
     * This method is invoked right before an action is to be executed (after all possible filters.)
     * It checks the existence of the [[migrationPath]].
     *
     * @param \yii\base\Action $action the action to be executed.
     * @return boolean whether the action should continue to be executed.
     */
    public function beforeAction($action)
    {
        if (parent::beforeAction($action)) {
            $this->db = Instance::ensure($this->db, Connection::className());

            return true;
        } else {
            return false;
        }
    }

    /**
     * Enironment initialization action.
     * This is just a wrapper for other separate actions.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     */     
    public function actionEnvironment()
    {
        $exitCode = Yii::$app->runAction('init/env-project');
        $exitCode = Yii::$app->runAction('init/env-env') && $exitCode;
        $exitCode = Yii::$app->runAction('init/env-db') && $exitCode;

        return $exitCode;
    }

    /**
     * General initialization action.
     * This is just a wrapper for other separate actions.
     * This one must be called only after 'environment' action is done.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     */
    public function actionInitialize()
    {
        return Yii::$app->runAction('init/migrate-up');
    }

    /**
     * Initialize project.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     */
    public function actionEnvProject()
    {
        $str = Console::ansiFormat('Do you want to initialize(reinitialize) project?', [$this->colorNotable]);
        $confirm = $this->confirm($str, false);

        if ($confirm) {
            $this->stdout('Initializing project...', $this->colorDefault);

            $this->stdout('Copying .env file from .env.example template', $this->colorDefault);
            Initializer::copyFile(".env.example", ".env", null);

            $this->stdout('Setting folders permissions', $this->colorDefault);
            Initializer::setPermissions([
                'app/back/runtime' => "0777",
                'app/front/runtime' => "0777",
                'app/console/runtime' => "0777",
                'public/admin/assets' => "0777",
                'public/assets' => "0777",
                'public/storage' => "0777",
                'yii' => "0755",
            ]);

            $this->stdout('Setting cookie validation keys', $this->colorDefault);
            Initializer::setCookieValidationKey([".env" => "<cookie_validation_key>"]);
        } else {
            $this->stdout('OK, project is not touched', $this->colorSuccess);

            return Controller::EXIT_CODE_ERROR;
        }

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Set environment depending variables.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     * @uses \console\helpers\Initializer::setEnv()
     */
    public function actionEnvEnv()
    {
        $str = Console::ansiFormat('Which environment to use? (DEV|PROD)', [$this->colorNotable]);
        $answer = $this->prompt($str, ['default' => 'PROD']);

        if (!strncasecmp($answer, 'd', 1)) {
            $this->stdout('Setting DEV environment...', $this->colorDefault);
            Initializer::setEnv('dev');
        } elseif (!strncasecmp($answer, 'p', 1)) {
            $this->stdout('Setting PROD environment...', $this->colorDefault);
            Initializer::setEnv('prod');
        } else {
            $this->stderr('Environment you choose does not exist', $this->colorError);

            return Controller::EXIT_CODE_ERROR;
        }

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Set database variables in the .env file.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     * @uses \console\helpers\Initializer::setEnvVar()
     */
    public function actionEnvDb()
    {
        $dbHostPrompt = Console::ansiFormat('Enter database host name: ', [$this->colorNotable]);
        $dbHost = $this->prompt($dbHostPrompt, ['default' => env('DB_HOST', 'localhost')]);

        $dbPortPrompt = Console::ansiFormat('Enter database host name: ', [$this->colorNotable]);
        $dbPort = $this->prompt($dbPortPrompt, ['default' => env('DB_PORT', '3306')]);

        $dbNamePrompt = Console::ansiFormat('Enter database name: ', [$this->colorNotable]);
        $dbName = $this->prompt($dbNamePrompt, ['default' => env('DB_NAME', 'app_db')]);

        $dbUserPrompt = Console::ansiFormat('Enter database user name: ', [$this->colorNotable]);
        $dbUser = $this->prompt($dbUserPrompt, ['default' => env('DB_USERNAME', 'root')]);

        $dbPassPrompt = Console::ansiFormat('Enter database user password: ', [$this->colorNotable]);
        $dbPass = $this->prompt($dbPassPrompt, ['default' => env('DB_PASSWORD', '')]);

        $dbCharsetPrompt = Console::ansiFormat('Enter database charset name: ', [$this->colorNotable]);
        $dbCharset = $this->prompt($dbCharsetPrompt, ['default' => env('DB_CHARSET', 'utf8')]);

        $this->stdout('Setting database...', $this->colorDefault);
        Initializer::setEnvVar('DB_HOST', $dbHost);
        Initializer::setEnvVar('DB_PORT', $dbPort);
        Initializer::setEnvVar('DB_NAME', $dbName);
        Initializer::setEnvVar('DB_USERNAME', $dbUser);
        Initializer::setEnvVar('DB_PASSWORD', $dbPass);
        Initializer::setEnvVar('DB_CHARSET', $dbCharset);

        $this->db->username = $dbUser;
        $this->db->password = $dbPass;
        $this->db->charset = $dbCharset;
        $this->db->dsn = "mysql:host=$dbHost;port=$dbPort;dbname=$dbName";

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Prepare application by running depending modules migrations.
     * Deals basically with database but can be also cache, assets and so on.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     */
    public function actionMigrateUp()
    {
        /***************************************************************************************************************
         * Getting input data from console user.
         **************************************************************************************************************/

        $emailStr = Console::ansiFormat('Enter admin user email:', [$this->colorNotable]);
        $email = $this->prompt($emailStr, [
            'default' => env('APP_ADMIN_EMAIL', 'admin@example.com'),
        ]);

        $usernameStr = Console::ansiFormat('Enter admin user name:', [$this->colorNotable]);
        $username = $this->prompt($usernameStr, [
            'default' => ArrayHelper::getValue(explode(',', env('APP_ADMINS', 'admin')), 0),
        ]);

        $passwordStr = Console::ansiFormat('Enter global admin user password:', [$this->colorNotable]);
        $password = $this->prompt($passwordStr, ['default' => '123456']);

        $this->askForInteraction();

        /***************************************************************************************************************
         * Applying application migrations.
         **************************************************************************************************************/

        $migrations = explode(',', env('APP_MIGRATION_LOOKUP', '@console/migrations'));

        try {
            foreach ($migrations as $migrationPath) {
                Yii::$app->runAction('migrate/up', [
                    'migrationPath' => $migrationPath,
                    'interactive' => $this->interactive,
                ]);
            }
        } catch (Exception $e) {
            $this->stdout("Exception: {$e->getMessage()}\n", $this->colorError);
            $this->stdout("({$e->getFile()}: {$e->getLine()})\n", $this->colorError);

            return Controller::EXIT_CODE_ERROR;
        }


        /***************************************************************************************************************
         * Creating and confirming account of the first user.
         **************************************************************************************************************/

        Yii::$app->runAction('user/create', [$email, $username, $password]);

        // Have to wait a bit until the user is inserted into the database.
        sleep(1);

        Yii::$app->runAction('user/confirm', [$email]);

        /***************************************************************************************************************
         * Clearing all cache.
         **************************************************************************************************************/

        Yii::$app->runAction('cache/flush-all');

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Revert depending modules migrations.
     * Revert changes made by 'migrate-up' action.
     * This method probably will not work if there are holes in migration history.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     */
    public function actionMigrateDown()
    {
        $this->askForInteraction();

        $this->db->createCommand('SET foreign_key_checks = 0;')->execute();

        /***************************************************************************************************************
         * Reverting application migrations.
         **************************************************************************************************************/

        $migrations = explode(',', env('APP_MIGRATION_LOOKUP', '@console/migrations'));

        // Migrations should be reverted in reversed order.
        $migrations = array_reverse($migrations);

        foreach ($migrations as $migrationPath) {
            $count = $this->countMigrations($migrationPath);
            if ($count > 0) {
                Yii::$app->runAction('migrate/down', [
                    $count,
                    'migrationPath' => $migrationPath,
                    'interactive' => $this->interactive,
                ]);
            }
        }

        $this->db->createCommand('SET foreign_key_checks = 1;')->execute();

        /***************************************************************************************************************
         * Clearing all cache
         **************************************************************************************************************/

        Yii::$app->runAction('cache/flush-all');

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Delete (reset) found database tables.
     *
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
     * @throws \yii\db\Exception
     */
    public function actionDestroyDb()
    {
        $str = Console::ansiFormat('Do you really want to destroy application database?', [$this->colorNotable]);
        $confirm = $this->confirm($str, false);

        if ($confirm) {
            $tables = Yii::$app->getDb()->schema->tableNames;
            if ($tables) {
                $this->db->createCommand('SET foreign_key_checks = 0;')->execute();
                foreach ($tables as $table) {
                    $this->stdout("Destroying $table table", $this->colorDefault);
                    $this->db->createCommand()->dropTable($table)->execute();
                }
                $this->db->createCommand('SET foreign_key_checks = 1;')->execute();
                $this->stdout('Successfully destroyed DB', $this->colorSuccess);
            } else {
                $this->stderr('No tables found', $this->colorError);
            }
        } else {
            $this->stdout('OK, Application database is not touched', $this->colorSuccess);
        }

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Run 'destroy-db', 'migrate-up' and 'insert-demo' actions without user interaction.
     *
     * @return int
     */
    public function actionReset()
    {
        Yii::$app->runAction('init/destroy-db', [
            'interactive' => false,
        ]);

        /*Yii::$app->runAction('init/down', [
            'interactive' => false,
        ]);*/

        Yii::$app->runAction('init/up', [
            'interactive' => false,
        ]);

        /*Yii::$app->runAction('app/insert-demo', [
            'interactive' => false,
        ]);*/

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Insert demo data into DB.
     *
     * @todo use Faker.
     * @throws \yii\db\Exception
     */
    public function actionInsertDemo()
    {
        $this->stdout('Inserting demo data...', $this->colorDefault);

        // $command = $this->db->createCommand();
        // $command->batchInsert($model::tableName(), $model->attributes(), [
        //     [...]
        //     [...]
        // ])->execute();

        $this->stdout('Inserting demo data finished', $this->colorDefault);

        return Controller::EXIT_CODE_NORMAL;
    }

    /**
     * Ask user for interaction.
     *
     * @return void
     */
    public function askForInteraction()
    {
        $str = Console::ansiFormat('Do you want to interact with this script?', [$this->colorNotable]);
        $this->interactive = $this->confirm($str, true);
    }

    /**
     * Count files in migration folder.
     *
     * @param $alias
     * @return int
     */
    public function countMigrations($alias)
    {
        return count(glob(Yii::getAlias("$alias/m*.php")));
    }
}