migration.abstract.class.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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
  48. ->column('version', 'varchar(100)')
  49. ->primaryKey('version')
  50. ->commit();
  51. switch($direction){
  52. case self::MIGRATE_UP:
  53. foreach(self::migrations() as $migration){
  54. if(!$migration::installed()){
  55. $pdo->transaction(function($pdo) use($migration, $table){
  56. $migration::up($pdo);
  57. $migration::change($pdo, self::MIGRATE_UP);
  58. });
  59. $table->insert(['version'=>$migration::version()]);
  60. if(!$migration::installed()){
  61. throw new \Exception("Migration {$migration::version()} {$direction} failed");
  62. }
  63. if(--$amount == 0){
  64. break;
  65. }
  66. }
  67. }
  68. break;
  69. case self::MIGRATE_DOWN:
  70. foreach(array_reverse(self::migrations()) as $migration){
  71. if($migration::installed()){
  72. $pdo->transaction(function($pdo) use($migration, $table){
  73. $migration::change($pdo, self::MIGRATE_DOWN);
  74. $migration::down($pdo);
  75. });
  76. $table->delete(['version'=>$migration::version()]);
  77. if($migration::installed()){
  78. throw new \Exception("Migration {$migration::version()} {$direction} failed");
  79. }
  80. if(--$amount == 0){
  81. break;
  82. }
  83. }
  84. }
  85. break;
  86. default:
  87. throw new \Exception("Invalid migration direction '{$direction}'");
  88. }
  89. }
  90. final public static function migrate_all(string $direction){
  91. self::migrate($direction, count(self::migrations()));
  92. }
  93. final public static function bind(string $dsn){
  94. self::$pdo = PDO::from($dsn);
  95. }
  96. final public static function release(){
  97. self::$pdo = null;
  98. }
  99. }
  100. ?>