Files
ezmigrationbundle/Core/StorageHandler/Database.php

292 lines
8.1 KiB
PHP

<?php
namespace Kaliop\eZMigrationBundle\Core\StorageHandler;
use Kaliop\eZMigrationBundle\API\StorageHandlerInterface;
use Kaliop\eZMigrationBundle\API\Collection\MigrationCollection;
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
use Doctrine\DBAL\Schema\Schema;
use eZ\Publish\Core\Persistence\Database\SelectQuery;
use Kaliop\eZMigrationBundle\API\Value\Migration;
/**
* Database-backed storage for info on executed migrations
*/
class Database implements StorageHandlerInterface
{
/**
* Flag to indicate that the migration version table has been created
*
* @var boolean
*/
private $migrationsTableExists = false;
/**
* Name of the database table where installed migration versions are tracked.
* @var string
*
* @todo add setter/getter, as we need to clear versionTableExists when switching this
*/
private $migrationsTableName;
/**
* @var DatabaseHandler $connection
*/
protected $connection;
/**
* @param DatabaseHandler $connection
* @param string $migrationsTableName
*/
public function __construct(DatabaseHandler $connection, $migrationsTableName = 'kaliop_migrations')
{
$this->connection = $connection;
$this->migrationsTableName = $migrationsTableName;
}
/**
* @return MigrationCollection
* @todo add support offset, limit
*/
public function loadMigrations()
{
$this->createMigrationsTableIfNeeded();
/** @var \eZ\Publish\Core\Persistence\Database\SelectQuery $q */
$q = $this->connection->createSelectQuery();
$q->select('migration, md5, path, execution_date, status')
->from($this->migrationsTableName)
->orderBy('migration', SelectQuery::ASC);
$stmt = $q->prepare();
$stmt->execute();
$results = $stmt->fetchAll();
$migrations = array();
foreach ($results as $result) {
$migrations[$result['migration']] = new Migration(
$result['migration'],
$result['md5'],
$result['path'],
$result['execution_date'],
$result['status']
);
}
return new MigrationCollection($migrations);
}
/**
* Check if the version db table exists and create it if not.
*
* @return bool true if table has been created, false if it was already there
*
* @todo add a 'force' flag to force table re-creation
* @todo manage changes to table definition
*/
public function createMigrationsTableIfNeeded()
{
if ($this->migrationsTableExists) {
return false;
}
if ($this->tableExist($this->migrationsTableName)) {
$this->migrationsTableExists = true;
return false;
}
$this->createMigrationsTable();
$this->migrationsTableExists = true;
return true;
}
public function createMigrationsTable()
{
/** @var \Doctrine\DBAL\Schema\AbstractSchemaManager $sm */
$sm = $this->connection->getConnection()->getSchemaManager();
$dbPlatform = $sm->getDatabasePlatform();
$schema = new Schema();
$t = $schema->createTable($this->migrationsTableName);
$t->addColumn('migration', 'string', array('length' => 255));
$t->addColumn('path', 'string', array('length' => 4000));
$t->addColumn('md5', 'string', array('length' => 32));
$t->addColumn('execution_date', 'integer', array('notnull' => false));
$t->addColumn('status', 'integer', array('default ' => Migration::STATUS_TODO));
$t->setPrimaryKey(array('migration'));
foreach($schema->toSql($dbPlatform) as $sql) {
$this->connection->exec($sql);
}
}
/**
* Check if a table exists in the database
*
* @param string $tableName
* @return bool
*/
protected function tableExist($tableName)
{
/** @var \Doctrine\DBAL\Schema\AbstractSchemaManager $sm */
$sm = $this->connection->getConnection()->getSchemaManager();
foreach($sm->listTables() as $table) {
if($table->getName() == $tableName) {
return true;
}
}
return false;
}
/// *** BELOW THE FOLD: TO BE REFACTORED ***
/**
* Return the array of registered versions
*
* @return array
*/
public function getVersions()
{
return $this->versions;
}
/**
* Mark a version as migrated in the database
*
* @param string $bundle
* @param int $version
*/
public function markVersionMigrated($bundle, $version)
{
$this->createVersionTableIfNeeded();
/** @var $q \ezcQueryInsert */
$q = $this->connection->createInsertQuery();
$q->insertInto($this->versionTableName)
->set('bundle', $q->bindValue($bundle))
->set('version', $q->bindValue($version));
$stmt = $q->prepare();
$stmt->execute();
}
/**
* Mark a version as NOT migrated in the database
*
* @param string $bundle
* @param int $version
*/
public function markVersionNotMigrated($bundle, $version)
{
$this->createVersionTableIfNeeded();
/** @var $q \ezcQueryDelete */
$q = $this->connection->createDeleteQuery();
$q->deleteFrom($this->versionTableName)
->where(
$q->expr->lAnd(
$q->expr->eq('bundle', $q->bindValue($bundle)),
$q->expr->eq('version', $q->bindValue($version))
)
);
$stmt = $q->prepare();
$stmt->execute();
}
/**
* Return the migrated versions grouped by bundle.
*
* The list is based on the values in the version table in the database.
*
* @return array key: bundle name,
*/
public function getMigratedVersions()
{
$this->createVersionTableIfNeeded();
/** @var $q \ezcQuerySelect */
$q = $this->connection->createSelectQuery();
$q->select('bundle, version')->from($this->versionTableName);
$stmt = $q->prepare();
$stmt->execute();
$results = $stmt->fetchAll();
$bundleVersions = array();
foreach ($results as $result) {
$bundleVersions[$result['bundle']][] = $result['version'];
}
return $bundleVersions;
}
/**
* Method to get the current version for a specific bundle
*
* @param string $bundle The bundle we want the version for
* @return int The version or 0 if no version can be found
*/
public function getCurrentVersionByBundle($bundle)
{
$this->createVersionTableIfNeeded();
/** @var $q \ezcQuerySelect */
$q = $this->connection->createSelectQuery();
$q->select('version')
->from($this->versionTableName);
if ($this->versions && $this->versions[$bundle]) {
$migratedVersions = array_keys($this->versions[$bundle]);
$q->where(
$q->expr->lAnd(
$q->expr->eq('bundle', $bundle),
$q->expr->in($q->bindValue(implode(',', $migratedVersions)))
)
);
} else {
$q->where(
$q->expr->eq('bundle', $q->bindValue($bundle))
);
}
$q->limit(1, 0)
->orderBy('version', \ezcQuerySelect::DESC);
$stmt = $q->prepare();
$stmt->execute();
$result = $stmt->fetchColumn(0);
return $result !== false ? (int)$result : '0';
}
/**
* Helper function to get all the table names from the database
*
* @todo Make this DB independent
* @return array
*/
protected function listTableNames()
{
$results = array();
$stmt = $this->connection->prepare("SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'");
$stmt->execute();
$tables = $stmt->fetchAll();
foreach ($tables as $table) {
$results[] = array_shift($table);
}
return array_values($results);
}
}