123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- <?php
- namespace Juju\PDO;
- require_once(realpath(dirname(__DIR__).'/pdo.class.php'));
- use Juju\{PDO, PDO\Transaction, Settings};
- abstract class Migration {
- public abstract static function up(Transaction $pdo);
- public abstract static function down(Transaction $pdo);
- public abstract static function change(Transaction $pdo, string $direction);
- const MIGRATE_UP = 'up';
- const MIGRATE_DOWN = 'down';
- private static $pdo;
- private static $last_date;
- private static $initial_date;
- private static $current_version;
- private static $initial_version;
- 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 get_current_version(bool $force = false){
- if(is_null(self::$current_version) || $force){
- if(is_null(self::$pdo)){
- throw new \Exception("Migration is not bound to a data source");
- }
- $table_name = self::version_table();
- $query = self::$pdo->query("select max(version) version from `{$table_name}`");
- self::$current_version = $query->fetch()['version'];
- $query->closeCursor();
- }
- return self::$current_version;
- }
- final public static function get_initial_version(bool $force = false){
- if(is_null(self::$initial_version) || $force){
- if(is_null(self::$pdo)){
- throw new \Exception("Migration is not bound to a data source");
- }
- $table_name = self::version_table();
- $query = self::$pdo->query("select min(version) version from `{$table_name}`");
- self::$initial_version = $query->fetch()['version'];
- $query->closeCursor();
- }
- return self::$initial_version;
- }
- final public static function get_last_install_date(bool $force = false){
- if(is_null(self::$last_date) || $force){
- if(is_null(self::$pdo)){
- throw new \Exception("Migration is not bound to a data source");
- }
- $table_name = self::version_table();
- $query = self::$pdo->query("select max(date) date from `{$table_name}`");
- self::$last_date = $query->fetch()['date'];
- $query->closeCursor();
- }
- return self::$last_date;
- }
- final public static function get_initial_install_date(bool $force = false){
- if(is_null(self::$initial_date) || $force){
- if(is_null(self::$pdo)){
- throw new \Exception("Migration is not bound to a data source");
- }
- $table_name = self::version_table();
- $query = self::$pdo->query("select min(date) date from `{$table_name}`");
- self::$initial_date = $query->fetch()['date'];
- $query->closeCursor();
- }
- return self::$initial_date;
- }
- final public static function installed(){
- $pdo = self::$pdo;
- $count = $pdo->exec(
- "select 1 ".
- "from `".self::version_table()."` ".
- "where version = {$pdo->quote(static::version())}"
- );
- if($count === false){
- throw $pdo->getError();
- }
- return $count;
- }
- 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");
- }
- $pdo = self::$pdo;
- $table = self::$pdo->table(self::version_table());
- $table
- ->column('version', 'varchar(100)')
- ->column('date', 'datetime', DefaultValue::named('current_timestamp'))
- ->primaryKey('version')
- ->index('date_idx', ['date'])
- ->commit();
- switch($direction){
- case self::MIGRATE_UP:
- foreach(self::migrations() as $migration){
- if(!$migration::installed()){
- $pdo->transaction(function($pdo) use($migration, $table){
- $migration::up($pdo);
- $migration::change($pdo, self::MIGRATE_UP);
- });
- $table->insert(['version'=>$migration::version()]);
- if(!$migration::installed()){
- throw new \Exception("Migration {$migration::version()} {$direction} failed");
- }
- if(--$amount == 0){
- break;
- }
- }
- }
- break;
- case self::MIGRATE_DOWN:
- foreach(array_reverse(self::migrations()) as $migration){
- if($migration::installed()){
- $pdo->transaction(function($pdo) use($migration, $table){
- $migration::change($pdo, self::MIGRATE_DOWN);
- $migration::down($pdo);
- });
- $table->delete(['version'=>$migration::version()]);
- if($migration::installed()){
- throw new \Exception("Migration {$migration::version()} {$direction} failed");
- }
- if(--$amount == 0){
- break;
- }
- }
- }
- break;
- default:
- throw new \Exception("Invalid migration direction '{$direction}'");
- }
- self::get_initial_version(true);
- self::get_initial_install_date(true);
- self::get_current_version(true);
- self::get_last_install_date(true);
- }
- final public static function migrate_all(string $direction){
- self::migrate($direction, count(self::migrations()));
- }
- final public static function bind(string $dsn){
- self::$pdo = PDO::from($dsn);
- }
- final public static function release(){
- self::$pdo = null;
- }
- }
- ?>
|