migration.abstract.class.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. namespace Juju\PDO;
  3. require_once(realpath(dirname(__DIR__).'/pdo.class.php'));
  4. use Juju\{PDO, PDO\Transaction, Settings};
  5. abstract class Migration {
  6. public abstract static function up(Transaction $pdo);
  7. public abstract static function down(Transaction $pdo);
  8. public abstract static function change(Transaction $pdo, string $direction);
  9. const MIGRATE_UP = 'up';
  10. const MIGRATE_DOWN = 'down';
  11. private static $pdo;
  12. final public static function version(){
  13. $name = get_called_class();
  14. return substr($name, strrpos($name, '\\') + 1);
  15. }
  16. final public static function version_table(){
  17. if(!class_exists("Juju\\Settings")){
  18. throw new \Exception("Settings not loaded");
  19. }
  20. return Settings::get('db.versions');
  21. }
  22. final public static function installed(){
  23. $pdo = self::$pdo;
  24. $count = $pdo->exec(
  25. "select 1 ".
  26. "from `".self::version_table()."` ".
  27. "where version = {$pdo->quote(static::version())}"
  28. );
  29. if($count === false){
  30. throw $pdo->getError();
  31. }
  32. return $count;
  33. }
  34. final public static function migrations(){
  35. $migrations = array_filter(get_declared_classes(), function($class){
  36. return 0 === strpos($class, "Migration\\");
  37. });
  38. sort($migrations);
  39. return $migrations;
  40. }
  41. final public static function migrate(string $direction, int $amount = 1){
  42. if($amount < 1){
  43. throw new \Exception("Migration amount must be a positive integer");
  44. }
  45. $pdo = self::$pdo;
  46. $table = $pdo->table(self::version_table());
  47. $table->column('version', 'varchar(100)')
  48. ->primaryKey('version')
  49. ->commit();
  50. switch($direction){
  51. case self::MIGRATE_UP:
  52. foreach(self::migrations() as $migration){
  53. if(!$migration::installed()){
  54. $pdo->transaction(function($pdo) use($migration, $table){
  55. $migration::up($pdo);
  56. $migration::change($pdo, self::MIGRATE_UP);
  57. });
  58. $table->insert(['version'=>$migration::version()]);
  59. if(!$migration::installed()){
  60. throw new \Exception("Migration {$migration::version()} {$direction} failed");
  61. }
  62. if(--$amount == 0){
  63. break;
  64. }
  65. }
  66. }
  67. break;
  68. case self::MIGRATE_DOWN:
  69. foreach(array_reverse(self::migrations()) as $migration){
  70. if($migration::installed()){
  71. $pdo->transaction(function($pdo) use($migration, $table){
  72. $migration::change($pdo, self::MIGRATE_DOWN);
  73. $migration::down($pdo);
  74. });
  75. $table->delete(['version'=>$migration::version()]);
  76. if($migration::installed()){
  77. throw new \Exception("Migration {$migration::version()} {$direction} failed");
  78. }
  79. if(--$amount == 0){
  80. break;
  81. }
  82. }
  83. }
  84. break;
  85. default:
  86. throw new \Exception("Invalid migration direction '{$direction}'");
  87. }
  88. }
  89. final public static function migrate_all(string $direction){
  90. self::migrate($direction, count(self::migrations()));
  91. }
  92. final public static function bind(string $dsn){
  93. self::$pdo = PDO::from($dsn);
  94. }
  95. final public static function release(){
  96. self::$pdo = null;
  97. }
  98. }
  99. ?>