|
@@ -0,0 +1,97 @@
|
|
|
|
+<?php
|
|
|
|
+ namespace Juju\SQL;
|
|
|
|
+ require_once(realpath(dirname(__DIR__).'/sql.class.php'));
|
|
|
|
+ use Juju\{SQL, Settings};
|
|
|
|
+
|
|
|
|
+ abstract class Migration {
|
|
|
|
+ public abstract static function up();
|
|
|
|
+ public abstract static function down();
|
|
|
|
+ public abstract static function change();
|
|
|
|
+
|
|
|
|
+ const MIGRATE_UP = 'up';
|
|
|
|
+ const MIGRATE_DOWN = 'down';
|
|
|
|
+ private static $sql;
|
|
|
|
+
|
|
|
|
+ final public static function version(){
|
|
|
|
+ $name = get_called_class();
|
|
|
|
+ return substr($name, strrpos($name, '\\') + 1);
|
|
|
|
+ }
|
|
|
|
+ final public static function version_table(){
|
|
|
|
+ if(!class_exists("Juju\\Settings")){
|
|
|
|
+ throw new \Exception("Settings not loaded");
|
|
|
|
+ }
|
|
|
|
+ return Settings::get('db.versions');
|
|
|
|
+ }
|
|
|
|
+ final public static function installed(){
|
|
|
|
+ return self::$sql->query(
|
|
|
|
+ "select count(1) count ".
|
|
|
|
+ "from `".self::version_table()."` ".
|
|
|
|
+ "where version = ?",
|
|
|
|
+ 's',
|
|
|
|
+ static::version()
|
|
|
|
+ )->assoc_result['count'] == 1;
|
|
|
|
+ }
|
|
|
|
+ final public static function migrations(){
|
|
|
|
+ $migrations = array_filter(get_declared_classes(), function($class){
|
|
|
|
+ return 0 === strpos($class, "Migration\\");
|
|
|
|
+ });
|
|
|
|
+ sort($migrations);
|
|
|
|
+ return $migrations;
|
|
|
|
+ }
|
|
|
|
+ final public static function migrate(string $direction, int $amount = 1){
|
|
|
|
+ if($amount < 1){
|
|
|
|
+ throw new \Exception("Migration amount must be a positive integer");
|
|
|
|
+ }
|
|
|
|
+ $table = self::version_table();
|
|
|
|
+ $sql = self::$sql;
|
|
|
|
+ if(count($sql->query("show tables like '{$table}'")->assoc_results) == 0){
|
|
|
|
+ $sql->query("CREATE TABLE `{$table}` (version varchar(100) NOT NULL, primary key (version))")->execute();
|
|
|
|
+ }
|
|
|
|
+ switch($direction){
|
|
|
|
+ case self::MIGRATE_UP:
|
|
|
|
+ foreach(self::migrations() as $migration){
|
|
|
|
+ if(!$migration::installed()){
|
|
|
|
+ $migration::up();
|
|
|
|
+ $sql->query(
|
|
|
|
+ "insert into `{$table}` set version = ?",
|
|
|
|
+ 's',
|
|
|
|
+ $migration::version()
|
|
|
|
+ )->execute();
|
|
|
|
+ if(--$amount == 0){
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case self::MIGRATE_DOWN:
|
|
|
|
+ foreach(array_reverse(self::migrations()) as $migration){
|
|
|
|
+ if($migration::installed()){
|
|
|
|
+ $migration::down();
|
|
|
|
+ $sql->query(
|
|
|
|
+ "delete from `{$table}` where version = ?",
|
|
|
|
+ 's',
|
|
|
|
+ $migration::version()
|
|
|
|
+ )->execute();
|
|
|
|
+ if(--$amount == 0){
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ throw new \Exception("Invalid migration direction '{$direction}'");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ final public static function migrate_all(string $direction){
|
|
|
|
+ self::migrate($direction, count(self::migrations()));
|
|
|
|
+ }
|
|
|
|
+ final public static function bind(string $dsn){
|
|
|
|
+ self::$sql = SQL::FromDSN($dsn);
|
|
|
|
+ }
|
|
|
|
+ final public static function release(){
|
|
|
|
+ $sql = self::$sql;
|
|
|
|
+ unset($sql);
|
|
|
|
+ self::$sql = null;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+?>
|