I want to migrate
up
/down
the database using the code
instead of the console window. I have looked into the framework
. I have tried the following code:
$runner = new yii\console\Application([
'id' => 'auto-migrate',
'basePath' => dirname(__DIR__)
]);
$runner->runAction('migrate');
ob_start();
return htmlentities(ob_get_clean(), null, Yii::$app->charset);
It gives the Internal Server Error
. And doesn't even migrates the files to the database. But if the directory doesn't exists, it creates the directory. It is behaving the way it should but if the migration file exist in that same directory it gives Internal Server Error
.
I would probably do that over php function exec, but if you really want to do it by creating new application instance then you should be able to do it like this.
Case 1 - not very good idea
//Define, if you want to capture output
defined('STDIN') or define('STDIN', fopen('php://input', 'r'));
defined('STDOUT') or define('STDOUT', fopen('php://output', 'w'));
//Create new console app, if you do not run it from root namespace you should use '\yii\console\Application'
$runner = new \yii\console\Application([
'id' => 'auto-migrate',
'controllerNamespace' => 'console\controllers', //for migrate command it should not matter but for other cases you must specify otherwise applocation cannot find your controllers
'basePath' => dirname(__DIR__ . '/../../console/config'), //This must point to your console config directory, when i run this in frontend sitecontroller i must add '/../../console/config',
'components' => [
'db' => [//If you want to call migrate you probably need some database component
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydb',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
],
],
]);
ob_start();
try
{
$runner->runAction('migrate/up');
}
catch(\Exception $ex)
{
echo $ex->getMessage();
}
return htmlentities(ob_get_clean(), null, Yii::$app->charset);
And result in my case:
Yii Migration Tool (based on Yii v2.0.1) No new migration found. Your system is up-to-date.
Case 2
ob_start();
try
{
$output = Array();
exec(__DIR__ . '/../../yii migrate/up', $output);
echo implode("
", $output);
}
catch(\Exception $ex)
{
echo $ex->getMessage();
}
return htmlentities(ob_get_clean(), null, Yii::$app->charset);
And result in my case:
Yii Migration Tool (based on Yii v2.0.1) No new migration found. Your system is up-to-date.
\Yii::$app->runAction('migrate', ['migrationPath' => '@yii/rbac/migrations/']);
You could create Console Application with loading Console config to run migration:
public function actionMigrate()
{
// Keep current application
$oldApp = \Yii::$app;
// Load Console Application config
$config = require \Yii::getAlias('@app'). '/config/console.php';
new \yii\console\Application($config);
$result = \Yii::$app->runAction('migrate', ['migrationPath' => '@app/migrations/', 'interactive' => false]);
// Revert application
\Yii::$app = $oldApp;
return;
}
Above sample code is for yii2-app-basic template, you could change path for yii2-app-advanced template.
For cases like this, I use this solution. I only implement the migrate/up
case. To execute any other migrate
command, you must configure the parameters of runAction()
.
use yii\console\controllers\MigrateController;
/*Suppose that config is an array with the following structure
[
'class' => 'yii\db\Connection',
'dsn' => "$dsn",
'username' => "$username",
'password' => "$password",
'charset' => 'utf8',
]
*/
public function migrateUp($config){
$migrate = new MigrateController('migrate', \Yii::$app);
/*The name of the table for keeping applied migration information*/
$migrate->migrationTable = 'migration';
/*At least one of `migrationPath` or `migrationNamespaces` should be specified.
For this example I use `migrationNamespaces` to find a specific set of migrations I want to execute.*/
$migrate->migrationNamespaces = [
'app\modules\v1\migrations'
];
$migrate->migrationPath = null;
/*The DB connection object to use when applying migrations*/
$migrate->db = $config;
/*Run migrations without asking questions*/
$migrate->interactive = false;
/*The individual commands ran within the migration will not be output to the console*/
$migrate->compact = true;
/*php://temp is a read-write stream that allow temporary data to be stored in a file-like wrapper */
define('STDOUT', $fp= fopen('php://temp', 'r+'));
/*run migrate/up */
$status = $migrate->runAction('up');
/*Rewind the position of the file pointer*/
rewind($fp);
$migrationOutput = stream_get_contents($fp);
if (isset($status)){
ob_get_clean();
return ['Errors when executing migrations', $migrationOutput];
}
return ['Everything ok', $migrationOutput];
}
Shared Hosting using Yii2's Advanced Template - Console app within web app I have successfully applied the above code with a few tweaks.
Single domain multi/web with subfolders eg. company1/web
multi/web/index.php
/.htaccess
/assets
/common/config/main.php
/common/config/bootstrap.php
/config/main-local.php has
/only db and urlManager baseUrl=''
/console/config/main.php
/frontend/config/main.php
/images
/company1/web/assets
/company1/web/images
/company1/web/.htaccess
/company1/web/index.php
/company1/config/main-local.php
/company2/web/assets
/company2/web/images
/company2/web/.htaccess
/company2/web/index.php
/company2/config/main-local.php
/company3/web/assets
/company3/web/images
/company3/web/.htaccess
/company3/web/index.php
/company3/config/main-local.php
/vendor
Each company config main-local contains only the db and UrlManager components. The baseUrl of the UrlManager eg. '/company3/web/'
multi/web index.php is configured as follows:
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
defined('YII_CONSOLE') or define('YII_CONSOLE', false);
//vendors from composer.json
require (__DIR__ . '/vendor/autoload.php');
//yii framework
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
//common frontend bower and installer aliases
require(__DIR__ . '/common/config/bootstrap.php');
//empty
require(__DIR__ . '/frontend/config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
//vendor path, twilio, paypal
require(__DIR__ . '/common/config/main.php'),
//db=installer , urlmanager swiftmailer
require(__DIR__ . '/common/config/main-local.php'),
//etc... db and UrlManager Removed into above
require(__DIR__ . '/frontend/config/main.php'),
//cookie validation key
require(__DIR__ . '/frontend/config/main-local.php')
);
(new yii\web\Application($config))->run();
?>
Each company's subfolder's index.php is configured as follows:
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
defined('YII_CONSOLE') or define('YII_CONSOLE', false);
require(dirname(dirname(__DIR__)) . '/vendor/autoload.php');
require(dirname(dirname(__DIR__)) . '/vendor/yiisoft/yii2/Yii.php');
//aliases eg. common frontend bower
require(dirname(dirname(__DIR__)) . '/common/config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
//vendor path components ...rbac
require(dirname(dirname(__DIR__)) . '/common/config/main.php'),
//customer's database and urlmanager and swiftmailer taken out of frontend/config/main.php
require(dirname(__DIR__) . '/config/main-local.php'),
//excluding components db and urlmanager included in above
require(dirname(dirname(__DIR__)) . '/frontend/config/main.php'),
//cookie validation key
require(dirname(dirname(__DIR__)) . '/frontend/config/main-local.php')
);
//
(new yii\web\Application($config))->run();
``````
I have created an action in my Site controller to perform the console migration within the actionMigrateto_company_database.
<?php
public function actionMigrateto_company_database()
{
ob_start();
defined('STDIN') or define('STDIN', fopen('php://input', 'r'));
defined('STDOUT') or define('STDOUT', fopen('php://output', 'w'));
defined('STDERR') or define('STDERR', fopen('php://stderr', 'w'));
$oldApp = \Yii::$app;
// Load Console Application config
$config = yii\helpers\ArrayHelper::merge(
//migration namespaces and
//namespaces included in individual
//migration files
require \Yii::getAlias('@console'). '/config/main.php',
//vendor path components ...rbac
require \Yii::getAlias('@common').'/config/main.php',
//database
require (dirname(dirname(__DIR__)) .dirname(Yii::$app->urlManager->baseUrl). '/config/main-local.php'));
$runner = new \yii\console\Application($config);
$runner->runAction('migrate',['db','interactive' => 0]);
fclose(\STDOUT);
fclose(\STDIN);
fclose(\STDERR);
\Yii::$app = $oldApp;
return ob_get_clean();
}
?>
Adjust the frontend\views\layouts\main.php with
if (Yii::$app->UrlManager->baseUrl === '') and
if (Yii::$app->UrlManager->baseUrl <> '') statements.
.htaccess files are all as follows:
`````
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
`````