Adding migrations functionality to PHPCI using Phinx

This commit is contained in:
Dan Cryer 2014-05-13 15:15:06 +00:00
parent a62307250f
commit d63a029b74
10 changed files with 608 additions and 90 deletions

6
.gitignore vendored
View file

@ -10,4 +10,8 @@ config.php
PHPCI/config.yml
cache
/loggerconfig.php
/pluginconfig.php
/pluginconfig.php
PHPCI/Model/Migration.php
PHPCI/Model/Base/MigrationBase.php
PHPCI/Store/MigrationStore.php
PHPCI/Store/Base/MigrationStoreBase.php

View file

@ -204,19 +204,7 @@ class InstallCommand extends Command
{
$output->write('Setting up your database... ');
// Load PHPCI's bootstrap file:
require(PHPCI_DIR . 'bootstrap.php');
try {
// Set up the database, based on table data from the models:
$gen = new Database\Generator(Database::getConnection(), 'PHPCI', './PHPCI/Model/Base/');
$gen->generate();
} catch (Exception $ex) {
$output->writeln('');
$output->writeln('<error>PHPCI failed to set up the database.</error>');
$output->writeln('<error>' . $ex->getMessage() . '</error>');
die;
}
shell_exec(PHPCI_DIR . 'vendor/bin/phinx migrate -c "' . PHPCI_DIR . 'phinx.php"');
$output->writeln('<info>OK</info>');
}

View file

@ -50,11 +50,9 @@ class UpdateCommand extends Command
{
$this->verifyInstalled($output);
$output->writeln('Updating PHPCI database.');
$output->write('Updating PHPCI database: ');
// Update the database:
$gen = new \b8\Database\Generator(\b8\Database::getConnection(), 'PHPCI', './PHPCI/Model/Base/');
$gen->generate();
shell_exec(PHPCI_DIR . 'vendor/bin/phinx migrate -c "' . PHPCI_DIR . 'phinx.php"');
$output->writeln('<info>Done!</info>');
}

View file

@ -0,0 +1,218 @@
<?php
use Phinx\Migration\AbstractMigration;
/**
* Initial migration to create a PHPCI v1.2 database.
*/
class InitialMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// Set up tables:
$this->createBuildTable();
$this->createBuildMetaTable();
$this->createProjectTable();
$this->createUserTable();
// Set up foreign keys:
$build = $this->table('build');
if (!$build->hasForeignKey('project_id')) {
$build->addForeignKey('project_id', 'project', 'id', array('delete'=> 'CASCADE', 'update' => 'CASCADE'));
}
$build->save();
$buildMeta = $this->table('build_meta');
if (!$buildMeta->hasForeignKey('build_id')) {
$buildMeta->addForeignKey('build_id', 'build', 'id', array('delete'=> 'CASCADE', 'update' => 'CASCADE'));
}
if (!$buildMeta->hasForeignKey('project_id')) {
$buildMeta->addForeignKey('project_id', 'project', 'id', array('delete'=> 'CASCADE', 'update' => 'CASCADE'));
}
$buildMeta->save();
}
/**
* Migrate Down.
*/
public function down()
{
}
protected function createBuildTable()
{
$table = $this->table('build');
if (!$table->hasColumn('project_id')) {
$table->addColumn('project_id', 'integer');
}
if (!$table->hasColumn('commit_id')) {
$table->addColumn('commit_id', 'string', array('limit' => 50));
}
if (!$table->hasColumn('status')) {
$table->addColumn('status', 'integer', array('limit' => 4));
}
if (!$table->hasColumn('log')) {
$table->addColumn('log', 'text');
}
if (!$table->hasColumn('branch')) {
$table->addColumn('branch', 'string', array('limit' => 50));
}
if (!$table->hasColumn('created')) {
$table->addColumn('created', 'datetime');
}
if (!$table->hasColumn('started')) {
$table->addColumn('started', 'datetime');
}
if (!$table->hasColumn('finished')) {
$table->addColumn('finished', 'datetime');
}
if (!$table->hasColumn('committer_email')) {
$table->addColumn('committer_email', 'string', array('limit' => 250));
}
if (!$table->hasColumn('commit_message')) {
$table->addColumn('commit_message', 'text');
}
if (!$table->hasColumn('extra')) {
$table->addColumn('extra', 'text');
}
if ($table->hasColumn('plugins')) {
$table->removeColumn('plugins');
}
if (!$table->hasIndex(array('project_id'))) {
$table->addIndex(array('project_id'));
}
if (!$table->hasIndex(array('status'))) {
$table->addIndex(array('status'));
}
$table->save();
}
protected function createBuildMetaTable()
{
$table = $this->table('build_meta');
if (!$table->hasColumn('project_id')) {
$table->addColumn('project_id', 'integer');
}
if (!$table->hasColumn('build_id')) {
$table->addColumn('build_id', 'integer');
}
if (!$table->hasColumn('meta_key')) {
$table->addColumn('meta_key', 'string', array('limit' => 250));
}
if (!$table->hasColumn('meta_value')) {
$table->addColumn('meta_value', 'text');
}
if (!$table->hasIndex(array('build_id', 'meta_key'))) {
$table->addIndex(array('build_id', 'meta_key'));
}
$table->save();
}
protected function createProjectTable()
{
$table = $this->table('project');
if (!$table->hasColumn('title')) {
$table->addColumn('title', 'string', array('limit' => 250));
}
if (!$table->hasColumn('reference')) {
$table->addColumn('reference', 'string', array('limit' => 250));
}
if (!$table->hasColumn('git_key')) {
$table->addColumn('git_key', 'text');
}
if (!$table->hasColumn('public_key')) {
$table->addColumn('public_key', 'text');
}
if (!$table->hasColumn('type')) {
$table->addColumn('type', 'string', array('limit' => 50));
}
if (!$table->hasColumn('access_information')) {
$table->addColumn('access_information', 'string', array('limit' => 250));
}
if (!$table->hasColumn('last_commit')) {
$table->addColumn('last_commit', 'string', array('limit' => 250));
}
if (!$table->hasColumn('build_config')) {
$table->addColumn('build_config', 'text');
}
if (!$table->hasColumn('allow_public_status')) {
$table->addColumn('allow_public_status', 'integer');
}
if ($table->hasColumn('token')) {
$table->removeColumn('token');
}
if (!$table->hasIndex(array('title'))) {
$table->addIndex(array('title'));
}
$table->save();
}
protected function createUserTable()
{
$table = $this->table('user');
if (!$table->hasColumn('email')) {
$table->addColumn('email', 'string', array('limit' => 250));
}
if (!$table->hasColumn('hash')) {
$table->addColumn('hash', 'string', array('limit' => 250));
}
if (!$table->hasColumn('name')) {
$table->addColumn('name', 'string', array('limit' => 250));
}
if (!$table->hasColumn('is_admin')) {
$table->addColumn('is_admin', 'integer');
}
if (!$table->hasIndex(array('email'))) {
$table->addIndex(array('email'));
}
$table->save();
}
}

View file

@ -52,6 +52,7 @@ class BuildMetaBase extends Model
'meta_value' => 'getMetaValue',
// Foreign key getters:
'Project' => 'getProject',
'Build' => 'getBuild',
);
@ -67,6 +68,7 @@ class BuildMetaBase extends Model
'meta_value' => 'setMetaValue',
// Foreign key setters:
'Project' => 'setProject',
'Build' => 'setBuild',
);
@ -110,12 +112,20 @@ class BuildMetaBase extends Model
public $indexes = array(
'PRIMARY' => array('unique' => true, 'columns' => 'id'),
'idx_meta_id' => array('unique' => true, 'columns' => 'build_id, meta_key'),
'project_id' => array('columns' => 'project_id'),
);
/**
* @var array
*/
public $foreignKeys = array(
'build_meta_ibfk_1' => array(
'local_col' => 'project_id',
'update' => 'CASCADE',
'delete' => 'CASCADE',
'table' => 'project',
'col' => 'id'
),
'fk_meta_build_id' => array(
'local_col' => 'build_id',
'update' => 'CASCADE',
@ -281,6 +291,63 @@ class BuildMetaBase extends Model
$this->_setModified('meta_value');
}
/**
* Get the Project model for this BuildMeta by Id.
*
* @uses \PHPCI\Store\ProjectStore::getById()
* @uses \PHPCI\Model\Project
* @return \PHPCI\Model\Project
*/
public function getProject()
{
$key = $this->getProjectId();
if (empty($key)) {
return null;
}
$cacheKey = 'Cache.Project.' . $key;
$rtn = $this->cache->get($cacheKey, null);
if (empty($rtn)) {
$rtn = Factory::getStore('Project', 'PHPCI')->getById($key);
$this->cache->set($cacheKey, $rtn);
}
return $rtn;
}
/**
* Set Project - Accepts an ID, an array representing a Project or a Project model.
*
* @param $value mixed
*/
public function setProject($value)
{
// Is this an instance of Project?
if ($value instanceof \PHPCI\Model\Project) {
return $this->setProjectObject($value);
}
// Is this an array representing a Project item?
if (is_array($value) && !empty($value['id'])) {
return $this->setProjectId($value['id']);
}
// Is this a scalar value representing the ID of this foreign key?
return $this->setProjectId($value);
}
/**
* Set Project - Accepts a Project model.
*
* @param $value \PHPCI\Model\Project
*/
public function setProjectObject(\PHPCI\Model\Project $value)
{
return $this->setProjectId($value->getId());
}
/**
* Get the Build model for this BuildMeta by Id.
*

View file

@ -36,10 +36,9 @@ class ProjectBase extends Model
'id' => null,
'title' => null,
'reference' => null,
'git_key' => null,
'public_key' => null,
'ssh_private_key' => null,
'ssh_public_key' => null,
'type' => null,
'token' => null,
'access_information' => null,
'last_commit' => null,
'build_config' => null,
@ -54,10 +53,9 @@ class ProjectBase extends Model
'id' => 'getId',
'title' => 'getTitle',
'reference' => 'getReference',
'git_key' => 'getGitKey',
'public_key' => 'getPublicKey',
'ssh_private_key' => 'getSshPrivateKey',
'ssh_public_key' => 'getSshPublicKey',
'type' => 'getType',
'token' => 'getToken',
'access_information' => 'getAccessInformation',
'last_commit' => 'getLastCommit',
'build_config' => 'getBuildConfig',
@ -74,10 +72,9 @@ class ProjectBase extends Model
'id' => 'setId',
'title' => 'setTitle',
'reference' => 'setReference',
'git_key' => 'setGitKey',
'public_key' => 'setPublicKey',
'ssh_private_key' => 'setSshPrivateKey',
'ssh_public_key' => 'setSshPublicKey',
'type' => 'setType',
'token' => 'setToken',
'access_information' => 'setAccessInformation',
'last_commit' => 'setLastCommit',
'build_config' => 'setBuildConfig',
@ -107,12 +104,12 @@ class ProjectBase extends Model
'length' => 250,
'default' => null,
),
'git_key' => array(
'ssh_private_key' => array(
'type' => 'text',
'nullable' => true,
'default' => null,
),
'public_key' => array(
'ssh_public_key' => array(
'type' => 'text',
'nullable' => true,
'default' => null,
@ -122,12 +119,6 @@ class ProjectBase extends Model
'length' => 50,
'default' => 1,
),
'token' => array(
'type' => 'varchar',
'length' => 50,
'nullable' => true,
'default' => null,
),
'access_information' => array(
'type' => 'varchar',
'length' => 250,
@ -202,25 +193,25 @@ class ProjectBase extends Model
}
/**
* Get the value of GitKey / git_key.
* Get the value of SshPrivateKey / ssh_private_key.
*
* @return string
*/
public function getGitKey()
public function getSshPrivateKey()
{
$rtn = $this->data['git_key'];
$rtn = $this->data['ssh_private_key'];
return $rtn;
}
/**
* Get the value of PublicKey / public_key.
* Get the value of SshPublicKey / ssh_public_key.
*
* @return string
*/
public function getPublicKey()
public function getSshPublicKey()
{
$rtn = $this->data['public_key'];
$rtn = $this->data['ssh_public_key'];
return $rtn;
}
@ -237,18 +228,6 @@ class ProjectBase extends Model
return $rtn;
}
/**
* Get the value of Token / token.
*
* @return string
*/
public function getToken()
{
$rtn = $this->data['token'];
return $rtn;
}
/**
* Get the value of AccessInformation / access_information.
*
@ -358,39 +337,39 @@ class ProjectBase extends Model
}
/**
* Set the value of GitKey / git_key.
* Set the value of SshPrivateKey / ssh_private_key.
*
* @param $value string
*/
public function setGitKey($value)
public function setSshPrivateKey($value)
{
$this->_validateString('GitKey', $value);
$this->_validateString('SshPrivateKey', $value);
if ($this->data['git_key'] === $value) {
if ($this->data['ssh_private_key'] === $value) {
return;
}
$this->data['git_key'] = $value;
$this->data['ssh_private_key'] = $value;
$this->_setModified('git_key');
$this->_setModified('ssh_private_key');
}
/**
* Set the value of PublicKey / public_key.
* Set the value of SshPublicKey / ssh_public_key.
*
* @param $value string
*/
public function setPublicKey($value)
public function setSshPublicKey($value)
{
$this->_validateString('PublicKey', $value);
$this->_validateString('SshPublicKey', $value);
if ($this->data['public_key'] === $value) {
if ($this->data['ssh_public_key'] === $value) {
return;
}
$this->data['public_key'] = $value;
$this->data['ssh_public_key'] = $value;
$this->_setModified('public_key');
$this->_setModified('ssh_public_key');
}
/**
@ -413,24 +392,6 @@ class ProjectBase extends Model
$this->_setModified('type');
}
/**
* Set the value of Token / token.
*
* @param $value string
*/
public function setToken($value)
{
$this->_validateString('Token', $value);
if ($this->data['token'] === $value) {
return;
}
$this->data['token'] = $value;
$this->_setModified('token');
}
/**
* Set the value of AccessInformation / access_information.
*
@ -516,4 +477,16 @@ class ProjectBase extends Model
{
return Factory::getStore('Build', 'PHPCI')->getByProjectId($this->getId());
}
/**
* Get BuildMeta models by ProjectId for this Project.
*
* @uses \PHPCI\Store\BuildMetaStore::getByProjectId()
* @uses \PHPCI\Model\BuildMeta
* @return \PHPCI\Model\BuildMeta[]
*/
public function getProjectBuildMetas()
{
return Factory::getStore('BuildMeta', 'PHPCI')->getByProjectId($this->getId());
}
}

View file

@ -44,6 +44,38 @@ class BuildMetaStoreBase extends Store
return null;
}
public function getByProjectId($value, $limit = null, $useConnection = 'read')
{
if (is_null($value)) {
throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.');
}
$add = '';
if ($limit) {
$add .= ' LIMIT ' . $limit;
}
$count = null;
$query = 'SELECT * FROM `build_meta` WHERE `project_id` = :project_id' . $add;
$stmt = Database::getConnection($useConnection)->prepare($query);
$stmt->bindValue(':project_id', $value);
if ($stmt->execute()) {
$res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$map = function ($item) {
return new BuildMeta($item);
};
$rtn = array_map($map, $res);
return array('items' => $rtn, 'count' => $count);
} else {
return array('items' => array(), 'count' => 0);
}
}
public function getByBuildId($value, $limit = null, $useConnection = 'read')
{
if (is_null($value)) {

View file

@ -34,7 +34,8 @@
"symfony/console" : "~2.1",
"psr/log": "~1.0",
"monolog/monolog": "~1.6",
"pimple/pimple": "~1.1"
"pimple/pimple": "~1.1",
"robmorgan/phinx": "*"
},
"require-dev": {

220
composer.lock generated
View file

@ -3,20 +3,20 @@
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "07244b4e81274ba07fdb9f0166c73678",
"hash": "4fafee12f4bcc7e7dac5f997b511eabb",
"packages": [
{
"name": "block8/b8framework",
"version": "1.1.2",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/Block8/b8framework.git",
"reference": "63a18f2fdc1dc31b657ea39ef841339d89f24ce8"
"reference": "2ae699b8b6a28752e9eebbd43c518fdc80a9d322"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Block8/b8framework/zipball/63a18f2fdc1dc31b657ea39ef841339d89f24ce8",
"reference": "63a18f2fdc1dc31b657ea39ef841339d89f24ce8",
"url": "https://api.github.com/repos/Block8/b8framework/zipball/2ae699b8b6a28752e9eebbd43c518fdc80a9d322",
"reference": "2ae699b8b6a28752e9eebbd43c518fdc80a9d322",
"shasum": ""
},
"require": {
@ -50,7 +50,7 @@
"mvc",
"php"
],
"time": "2014-04-01 15:30:13"
"time": "2014-05-13 09:41:11"
},
{
"name": "ircmaxell/password-compat",
@ -245,6 +245,63 @@
],
"time": "2012-12-21 11:40:51"
},
{
"name": "robmorgan/phinx",
"version": "v0.3.4",
"source": {
"type": "git",
"url": "https://github.com/robmorgan/phinx.git",
"reference": "5de112662b64d4d6ffa45d4dfc557c2a4c1620cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/robmorgan/phinx/zipball/5de112662b64d4d6ffa45d4dfc557c2a4c1620cd",
"reference": "5de112662b64d4d6ffa45d4dfc557c2a4c1620cd",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"symfony/class-loader": "2.*",
"symfony/config": "2.*",
"symfony/console": "2.*",
"symfony/yaml": "2.*"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
"squizlabs/php_codesniffer": "dev-phpcs-fixer"
},
"bin": [
"bin/phinx"
],
"type": "library",
"autoload": {
"psr-0": {
"Phinx": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Morgan",
"email": "robbym@gmail.com",
"homepage": "http://robmorgan.id.au",
"role": "Lead Developer"
}
],
"description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
"homepage": "http://phinx.org",
"keywords": [
"database",
"database migrations",
"db",
"migrations",
"phinx"
],
"time": "2014-04-27 15:25:07"
},
{
"name": "swiftmailer/swiftmailer",
"version": "v5.2.0",
@ -299,6 +356,108 @@
],
"time": "2014-05-08 08:11:19"
},
{
"name": "symfony/class-loader",
"version": "v2.4.4",
"target-dir": "Symfony/Component/ClassLoader",
"source": {
"type": "git",
"url": "https://github.com/symfony/ClassLoader.git",
"reference": "5101f3094903a95db3552a834a9c6585afc10d3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/ClassLoader/zipball/5101f3094903a95db3552a834a9c6585afc10d3b",
"reference": "5101f3094903a95db3552a834a9c6585afc10d3b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/finder": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\ClassLoader\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony ClassLoader Component",
"homepage": "http://symfony.com",
"time": "2014-04-16 10:34:31"
},
{
"name": "symfony/config",
"version": "v2.4.4",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/2effc67af6f21a0d267210b72d0b0b691d113528",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/filesystem": "~2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Config\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2014-04-22 08:11:06"
},
{
"name": "symfony/console",
"version": "v2.4.4",
@ -354,6 +513,55 @@
"homepage": "http://symfony.com",
"time": "2014-04-27 13:34:57"
},
{
"name": "symfony/filesystem",
"version": "v2.4.4",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2014-04-16 10:34:31"
},
{
"name": "symfony/yaml",
"version": "v2.4.4",

29
phinx.php Normal file
View file

@ -0,0 +1,29 @@
<?php
require_once(dirname(__FILE__) . '/bootstrap.php');
$writeServers = $config->get('b8.database.servers.write');
if (!is_array($writeServers)) {
$writeServers = array($writeServers);
}
$conf = array(
'paths' => array(
'migrations' => 'PHPCI/Migrations',
),
'environments' => array(
'default_migration_table' => 'migration',
'default_database' => 'phpci',
'phpci' => array(
'adapter' => 'mysql',
'host' => end($writeServers),
'name' => $config->get('b8.database.name'),
'user' => $config->get('b8.database.username'),
'pass' => $config->get('b8.database.password'),
),
),
);
return $conf;