Adding Chamilo upgrade process via CLI (using Console component and Doctrine migrations) see BT#5572

skala
Julio Montoya 12 years ago
parent 65d621f865
commit aae9e11da1
  1. 5
      main/inc/global.inc.php
  2. 17
      resources/config/prod.php
  3. 38
      resources/migrations/1.10/Version20121220233847.php
  4. 311
      src/ChamiloLMS/Command/Database/MigrationCommand.php
  5. 19
      src/ChamiloLMS/Migrations/Version10.php
  6. 19
      src/ChamiloLMS/Migrations/Version8.php
  7. 19
      src/ChamiloLMS/Migrations/Version9.php
  8. 4
      src/ChamiloLMS/Migrations/migrations.yml
  9. 66
      tests/doctrine_console/cli-config.php
  10. 5
      tests/doctrine_console/doctrine.php

@ -12,10 +12,10 @@
* - Debug (Using Monolog)
* - Redirecting to the main/install folder if the configuration.php file does not exists. *
*
* It's recommended that ALL Chamilo scripts include this file.
* It's recommended that ALL Chamilo scripts include this file.
* This script returns a $app Application instance so you have access to all the services.
*
* @package chamilo.include
* @package chamilo.include
* @todo isn't configuration.php renamed to configuration.inc.php yet?
* @todo use the $_configuration array for all the needed variables
*
@ -394,6 +394,7 @@ if (isset($_configuration['main_database'])) {
$sortableListener = new \Gedmo\Sortable\SortableListener();
$app['db.event_manager']->addEventSubscriber($sortableListener);
}
$app['is_admin'] = false;
//Creating a Chamilo service provider
use Silex\ServiceProviderInterface;

@ -8,12 +8,21 @@ $app['jquery_ui_theme'] = 'smoothness';
// Cache
$app['cache.path'] = api_get_path(SYS_ARCHIVE_PATH);
//Twig cache
$app['twig.cache.path'] = $app['cache.path'].'twig';
// Http cache
$app['http_cache.cache_dir'] = $app['cache.path'].'http';
// Doctrine ORM
$app['db.orm.proxies_dir'] = $app['cache.path'].'proxies_dir';
//Profiler
$app['profiler.cache_dir'] = $app['cache.path'].'profiler';
//Monolog log file
$app['chamilo.log'] = $app['cache.path'].'chamilo.log';
// Assetic
/*
$app['assetic.path_to_cache'] = $app['cache.path'] . DIRECTORY_SEPARATOR . 'assetic' ;
@ -73,6 +82,10 @@ if (!is_dir($app['db.orm.proxies_dir'])) {
@mkdir($app['db.orm.proxies_dir'], api_get_permissions_for_new_directories());
}
if (!is_dir($app['cache.path'].'twig')) {
@mkdir($app['cache.path'].'twig', api_get_permissions_for_new_directories());
if (!is_dir($app['twig.cache.path'])) {
@mkdir($app['twig.cache.path'], api_get_permissions_for_new_directories());
}
if (!is_dir($app['profiler.cache_dir'])) {
@mkdir($app['profiler.cache_dir'], api_get_permissions_for_new_directories());
}

@ -1,38 +0,0 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your need!
*/
class Version20121220233847 extends AbstractMigration
{
public function up(Schema $schema)
{
/*$this->addSql('CREATE TABLE pages (
id INTEGER NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
slug VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
PRIMARY KEY (id)
)');*/
$table = $schema->createTable('pages');
$table->addColumn('id', 'integer', array("unsigned" => true, 'autoincrement' => 'true'));
$table->addColumn('title', 'string');
$table->addColumn('slug', 'string');
$table->addColumn('content', 'text');
$table->addColumn('created', 'datetime');
$table->addColumn('updated', 'datetime');
$table->addOption('engine' , 'MyISAM');
$table->setPrimaryKey(array('id'));
}
public function down(Schema $schema)
{
//$this->addSql('DROP TABLE pages');
$schema->dropTable('pages');
}
}

@ -0,0 +1,311 @@
<?php
namespace ChamiloLMS\Command\Database;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
use Symfony\Component\Console\Input\ArrayInput;
/**
* Class MigrationCommand
*/
class MigrationCommand extends Console\Command\Command
{
protected function configure()
{
$this
->setName('migrations:migrate_chamilo')
->setDescription('Execute a chamilo migration to a specified version or the latest available version.')
->addArgument('version', InputArgument::REQUIRED, 'The version to migrate to.', null)
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.');
}
public function getMinVersionSupportedByInstall()
{
return key($this->availableVersions());
}
public function getVersionNumberList()
{
$versionList = $this->availableVersions();
$versionNumberList = array();
foreach ($versionList as $version => $info) {
$versionNumberList[] = $version;
}
return $versionNumberList;
}
public function availableVersions()
{
$versionList = array(
'1.8.8' => array(
'require_update' => true,
'pre' => 'migrate-db-1.8.7-1.8.8-pre.sql',
'post' => 'null',
'hook_to_version' => '8' //see ChamiloLMS\Migrations\Version8.php file
),
'1.8.8.2' => false,
'1.8.8.4' => false,
'1.8.8.6' => false,
'1.9.0' => array(
'require_update' => true,
'pre' => 'migrate-db-1.8.8-1.9.0-pre.sql',
'post' => 'null',
'hook_to_version' => '9'
),
'1.9.2' => false,
'1.9.4' => false,
'1.9.6' => false,
'1.10' => array(
'require_update' => true,
'pre' => 'migrate-db-1.9.0-1.10.0-pre.sql',
'post' => 'migrate-db-1.9.0-1.10.0-post.sql',
'hook_to_version' => '10'
)
);
return $versionList;
}
public function getAvailableVersionInfo($version)
{
$versionList = $this->availableVersions();
foreach ($versionList as $versionName => $versionInfo) {
if ($version == $versionName) {
return $versionInfo;
}
}
return false;
}
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$version = $input->getArgument('version');
$dryRun = $input->getOption('dry-run');
$minVersion = $this->getMinVersionSupportedByInstall();
$versionList = $this->availableVersions();
$versionNameList = $this->getVersionNumberList();
//Checking version
if (!in_array($version, $versionNameList)) {
$output->writeln("<comment>Version '$version' is not available</comment>");
$output->writeln("<comment>Available versions: </comment><info>".implode(', ', $versionNameList)."</info>");
exit;
}
global $_configuration;
$currentVersion = null;
//Checking root_sys and correct Chamilo version to install
if (!isset($_configuration['root_sys'])) {
$output->writeln("<comment>Can't migrate Chamilo. This is not a Chamilo folder installation.</comment>");
}
if (isset($_configuration['system_version']) &&
!empty($_configuration['system_version']) &&
$_configuration['system_version'] > $minVersion &&
$version > $_configuration['system_version']
) {
$currentVersion = $_configuration['system_version'];
} else {
$output->writeln("<comment>Please provide a version greater than your current installation > </comment><info>".$_configuration['system_version']."</info>");
exit;
}
//Too much questions?
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog->askConfirmation(
$output,
'<question>Are you sure you want to update Chamilo located here?</question> '.$_configuration['root_sys'].' (y/N)',
false
)
) {
return;
}
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog->askConfirmation(
$output,
'<question>Are you sure you want to update from version</question> <info>'.$_configuration['system_version'].'</info> <comment>to version </comment><info>'.$version.'</info> (y/N)',
false
)
) {
return;
}
$output->writeln('<comment>Migrating from Chamilo version: </comment><info>'.$_configuration['system_version'].'</info> <comment>to version <info>'.$version);
//Starting
$output->writeln('<comment>Starting migration for Chamilo portal located here: </comment><info>'.$_configuration['root_sys'].'</info>');
$oldVersion = $currentVersion;
foreach ($versionList as $versionItem => $versionInfo) {
if ($versionItem > $currentVersion && $versionItem <= $version) {
if (isset($versionInfo['require_update']) && $versionInfo['require_update'] == true) {
//Greater than my current version
$this->startMigration($oldVersion, $versionItem, $dryRun, $output);
$oldVersion = $versionItem;
} else {
$output->writeln("<comment>Version <info>'$versionItem'</info> does not need a DB migration</comment>");
}
}
}
}
/**
* @param $version
* @param $output
*/
public function startMigration($fromVersion, $toVersion, $dryRun, $output)
{
$output->writeln("<comment>Starting migration from version: </comment><info>$fromVersion</info><comment> to </comment><info>$toVersion ");
$installPath = api_get_path(SYS_CODE_PATH).'install/';
$versionInfo = $this->getAvailableVersionInfo($toVersion);
if (isset($versionInfo['pre']) && !empty($versionInfo['pre'])) {
$sqlToInstall = $installPath.$versionInfo['pre'];
if (file_exists($sqlToInstall)) {
//$result = $this->processSQL($sqlToInstall, $dryRun, $output);
$result = true;
$output->writeln("<comment>Executing file: <info>'$sqlToInstall'</info>");
if ($result) {
$command = $this->getApplication()->find('migrations:migrate');
$arguments = array(
'command' => 'migrations:migrate',
'version' => $versionInfo['hook_to_version'],
'--configuration' => api_get_path(SYS_PATH).'src/ChamiloLMS/Migrations/migrations.yml'
);
$input = new ArrayInput($arguments);
$command->run($input, $output);
$output->writeln("<comment>Migration ended succesfully</comment>");
}
}
}
}
private function processSQL($sqlFilePath, $dryRun, $output)
{
try {
$lines = 0;
$conn = $this->getHelper('main_database')->getConnection();
$output->writeln(sprintf("Processing file '<info>%s</info>'... ", $sqlFilePath));
$sqlList = $this->getSQLContents($sqlFilePath, 'main');
$conn->beginTransaction();
foreach ($sqlList as $query) {
if ($dryRun) {
$output->writeln($query);
} else {
//$output->writeln(' <comment>-></comment> ' . $query);
$conn->executeQuery($query);
}
$lines++;
}
$conn->commit();
if (!$dryRun) {
$output->writeln(sprintf('%d statements executed!', $lines) . PHP_EOL);
return true;
}
} catch (\Exception $e) {
$conn->rollback();
$output->write(sprintf('<error>Migration failed. Error %s</error>', $e->getMessage()));
throw $e;
}
return false;
}
/**
*
* @param $file
* @param $section
* @param bool $printErrors
*
* @return array|bool
*/
public function getSQLContents($file, $section, $printErrors = true)
{
//check given parameters
if (empty($file)) {
$error = "Missing name of file to parse in get_sql_file_contents()";
if ($printErrors) {
echo $error;
}
return false;
}
if (!in_array($section, array('main', 'user', 'stats', 'scorm', 'course'))) {
$error = "Section '$section' is not authorized in getSQLContents()";
if ($printErrors) {
echo $error;
}
return false;
}
$filepath = $file;
if (!is_file($filepath) or !is_readable($filepath)) {
$error = "File $filepath not found or not readable in getSQLContents()";
if ($printErrors) {
echo $error;
}
return false;
}
//read the file in an array
// Empty lines should not be executed as SQL statements, because errors occur, see Task #2167.
$file_contents = file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!is_array($file_contents) or count($file_contents) < 1) {
$error = "File $filepath looks empty in getSQLContents()";
if ($printErrors) {
echo $error;
}
return false;
}
//prepare the resulting array
$section_contents = array();
$record = false;
foreach ($file_contents as $index => $line) {
if (substr($line, 0, 2) == '--') {
//This is a comment. Check if section name, otherwise ignore
$result = array();
if (preg_match('/^-- xx([A-Z]*)xx/', $line, $result)) { //we got a section name here
if ($result[1] == strtoupper($section)) {
//we have the section we are looking for, start recording
$record = true;
} else {
//we have another section's header. If we were recording, stop now and exit loop
if ($record) {
break;
}
$record = false;
}
}
} else {
if ($record) {
if (!empty($line)) {
$section_contents[] = $line;
}
}
}
}
//now we have our section's SQL statements group ready, return
return $section_contents;
}
}

@ -0,0 +1,19 @@
<?php
namespace ChamiloLMS\Controller\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version10 extends AbstractMigration
{
public function up(Schema $schema)
{
$this->addSql('UPDATE settings_current SET selected_value = "1.10" WHERE variable = "chamilo_database_version"');
}
public function down(Schema $schema)
{
}
}

@ -0,0 +1,19 @@
<?php
namespace ChamiloLMS\Controller\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version8 extends AbstractMigration
{
public function up(Schema $schema)
{
$this->addSql('UPDATE settings_current SET selected_value = "1.8.8.14911.doc" WHERE variable = "chamilo_database_version"');
}
public function down(Schema $schema)
{
}
}

@ -0,0 +1,19 @@
<?php
namespace ChamiloLMS\Controller\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version9 extends AbstractMigration
{
public function up(Schema $schema)
{
$this->addSql('UPDATE settings_current SET selected_value = "1.9.0.18715.doc" WHERE variable = "chamilo_database_version"');
}
public function down(Schema $schema)
{
}
}

@ -0,0 +1,4 @@
name: Chamilo Migrations
migrations_namespace: ChamiloLMS\Controller\Migrations
table_name: chamilo_migration_versions
migrations_directory: src/ChamiloLMS/Migrations/

@ -1,5 +1,6 @@
<?php
require_once dirname(__FILE__).'/../../main/inc/global.inc.php';
error_reporting(-1);
$config = new \Doctrine\ORM\Configuration();
@ -12,11 +13,62 @@ AnnotationRegistry::registerFile(api_get_path(SYS_PATH)."vendor/doctrine/orm/lib
$reader = new AnnotationReader();
$driverImpl = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, array(api_get_path(SYS_PATH)."tests/doctrine_console/mapping"));
$config->setMetadataDriverImpl($driverImpl);
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');
$connectionOptions = array(
$courseList = CourseManager::get_real_course_list();
$connectionOptions = array();
if (!empty($courseList)) {
foreach ($courseList as $course) {
$connectionOptions['_chamilo_course_.'.$course['db_name']] = array(
'driver' => 'pdo_mysql',
'dbname' => $course['db_name'],
'user' => $_configuration['db_user'],
'password' => $_configuration['db_password'],
'host' => $_configuration['db_host'],
);
}
}
$connectionOptions['main_database'] = array(
'driver' => 'pdo_mysql',
'dbname' => $_configuration['main_database'],
'user' => $_configuration['db_user'],
'password' => $_configuration['db_password'],
'host' => $_configuration['db_host'],
);
$connectionOptions['statistics_database'] = array(
'driver' => 'pdo_mysql',
'dbname' => $_configuration['statistics_database'],
'user' => $_configuration['db_user'],
'password' => $_configuration['db_password'],
'host' => $_configuration['db_host'],
);
/*
$connectionOptions['scorm_database'] = array(
'driver' => 'pdo_mysql',
'dbname' => $_configuration['scorm_database'],
'user' => $_configuration['db_user'],
'password' => $_configuration['db_password'],
'host' => $_configuration['db_host'],
);*/
$connectionOptions['user_personal_database'] = array(
'driver' => 'pdo_mysql',
'dbname' => $_configuration['user_personal_database'],
'user' => $_configuration['db_user'],
'password' => $_configuration['db_password'],
'host' => $_configuration['db_host'],
);
$defaultConnection = array(
'driver' => 'pdo_mysql',
'dbname' => $_configuration['main_database'],
'user' => $_configuration['db_user'],
@ -24,7 +76,7 @@ $connectionOptions = array(
'host' => $_configuration['db_host'],
);
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
$em = \Doctrine\ORM\EntityManager::create($defaultConnection, $config);
//Fixes some errors
$platform = $em->getConnection()->getDatabasePlatform();
@ -33,9 +85,17 @@ $platform->registerDoctrineTypeMapping('set', 'string');
$helpers = array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em),
);
use Doctrine\DBAL\DriverManager;
$multipleEM = array();
foreach ($connectionOptions as $name => $connection) {
$em = \Doctrine\ORM\EntityManager::create($defaultConnection, $config);
//$helpers[$name] = new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em);
$helpers[$name] = new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection());
}
/*
To generate doctrine2 entities you must:

@ -7,6 +7,7 @@ require __DIR__ . '/cli-config.php';
$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\Common\Version::VERSION);
$cli->setCatchExceptions(true);
$helperSet = $cli->getHelperSet();
foreach ($helpers as $name => $helper) {
$helperSet->set($helper, $name);
@ -38,7 +39,9 @@ $cli->addCommands(array(
new \Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand(),
new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand()
new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand(),
new ChamiloLMS\Command\Database\MigrationCommand()
));
$cli->run();

Loading…
Cancel
Save