| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- <?php
- namespace common\helpers;
- use Yii;
- use yii\base\BaseObject;
- use yii\base\InvalidConfigException;
- use yii\db\MigrationInterface;
- use yii\web\NotFoundHttpException;
- use yii\web\UnprocessableEntityHttpException;
- /**
- * Class MigrateHelper
- * @package common\helpers
- * @author jianyan74 <751393839@qq.com>
- */
- class MigrateHelper
- {
- /**
- * 最长数据迁移数量
- */
- const MAX_NAME_LENGTH = 180;
- /**
- * 输出安装过程
- *
- * @var bool
- */
- protected static $compact = false;
- /**
- * 目录
- *
- * @addons/RfHelpers/console/migrations
- *
- * @var array
- */
- protected static $migrationPath = [];
- /**
- * 命名空间
- *
- * @var array
- */
- protected static $migrationNamespaces = [];
- /**
- * 数据迁移过程
- *
- * @var array
- */
- protected static $info = [];
- /**
- * 根据路径执行数据迁移
- *
- * @param array $path
- * @param bool $compact
- * @throws InvalidConfigException
- * @throws NotFoundHttpException
- * @throws UnprocessableEntityHttpException
- */
- public static function upByPath(array $path, $compact = false)
- {
- self::$migrationPath = $path;
- self::$compact = $compact;
- if (empty(self::$migrationPath)) {
- throw new InvalidConfigException('At least one of `migrationPath` should be specified.');
- }
- foreach (self::$migrationPath as $i => $path) {
- self::$migrationPath[$i] = Yii::getAlias($path);
- }
- return self::up();
- }
- /**
- *
- * 根据命名空间执行数据迁移
- *
- * @param array $namespaces
- * @param bool $compact
- * @throws InvalidConfigException
- * @throws NotFoundHttpException
- * @throws UnprocessableEntityHttpException
- */
- public static function upByNamespaces(array $namespaces, $compact = false)
- {
- self::$migrationNamespaces = $namespaces;
- self::$compact = $compact;
- if (empty(self::$migrationNamespaces)) {
- throw new InvalidConfigException('At least one of `migrationNamespaces` should be specified.');
- }
- foreach (self::$migrationNamespaces as $key => $value) {
- self::$migrationNamespaces[$key] = trim($value, '\\');
- }
- return self::up();
- }
- /**
- * 根据路径执行数据迁移
- *
- * @param array $path
- * @param bool $compact
- * @throws InvalidConfigException
- * @throws NotFoundHttpException
- * @throws UnprocessableEntityHttpException
- */
- public static function downByPath(array $path, $compact = false)
- {
- self::$migrationPath = $path;
- self::$compact = $compact;
- if (empty(self::$migrationPath)) {
- throw new InvalidConfigException('At least one of `migrationPath` should be specified.');
- }
- foreach (self::$migrationPath as $i => $path) {
- self::$migrationPath[$i] = Yii::getAlias($path);
- }
- return self::down();
- }
- /**
- *
- * 根据命名空间执行数据迁移
- *
- * @param array $namespaces
- * @param bool $compact
- * @throws InvalidConfigException
- * @throws NotFoundHttpException
- * @throws UnprocessableEntityHttpException
- */
- public static function downByNamespaces(array $namespaces, $compact = false)
- {
- self::$migrationNamespaces = $namespaces;
- self::$compact = $compact;
- if (empty(self::$migrationNamespaces)) {
- throw new InvalidConfigException('At least one of `migrationNamespaces` should be specified.');
- }
- foreach (self::$migrationNamespaces as $key => $value) {
- self::$migrationNamespaces[$key] = trim($value, '\\');
- }
- return self::down();
- }
- /**
- * @param int $limit
- * @throws InvalidConfigException
- * @throws NotFoundHttpException
- * @throws UnprocessableEntityHttpException
- */
- protected static function up($limit = 0)
- {
- $migrations = self::getNewMigrations();
- if (empty($migrations)) {
- throw new NotFoundHttpException('找不到可用的数据迁移');
- }
- $limit = (int)$limit;
- if ($limit > 0) {
- $migrations = array_slice($migrations, 0, $limit);
- }
- foreach ($migrations as $migration) {
- $nameLimit = static::MAX_NAME_LENGTH;
- if ($nameLimit !== null && strlen($migration) > $nameLimit) {
- throw new UnprocessableEntityHttpException("The migration name '$migration' is too long. Its not possible to apply this migration.");
- }
- }
- $applied = 0;
- foreach ($migrations as $migration) {
- if (!self::migrateUp($migration)) {
- throw new UnprocessableEntityHttpException( $migration . '迁移失败了。其余的迁移被取消');
- }
- $applied++;
- }
- return self::$info;
- }
- /**
- * @return array
- * @throws InvalidConfigException
- * @throws NotFoundHttpException
- * @throws UnprocessableEntityHttpException
- */
- protected static function down()
- {
- $migrations = self::getNewMigrations();
- if (empty($migrations)) {
- throw new NotFoundHttpException('找不到可用的数据迁移');
- }
- $reverted = 0;
- foreach ($migrations as $migration) {
- if (!self::migrateDown($migration)) {
- throw new UnprocessableEntityHttpException( $migration . '迁移失败了。其余的迁移被取消');
- }
- $reverted++;
- }
- self::$info[] = "Migrated down successfully.";
- return self::$info;
- }
- /**
- * @param $class
- * @return bool
- * @throws InvalidConfigException
- */
- protected static function migrateUp($class)
- {
- self::$info[] = "*** applying $class";
- // 打开输出缓冲区并获取内容
- ob_start();
- $start = microtime(true);
- $migration = self::createMigration($class);
- if ($migration->up() !== false) {
- $tmpInfo = explode('>', ob_get_contents());
- foreach ($tmpInfo as $item) {
- !empty(trim($item)) && self::$info[] = $item;
- }
- $time = microtime(true) - $start;
- self::$info[] = "*** applied $class (time: " . sprintf('%.3f', $time) . "s)";
- ob_end_clean();
- return true;
- }
- ob_end_clean();
- $time = microtime(true) - $start;
- self::$info[] = "*** failed to apply $class (time: " . sprintf('%.3f', $time) . "s)n";
- return false;
- }
- /**
- * @param $class
- * @return bool
- * @throws InvalidConfigException
- */
- protected static function migrateDown($class)
- {
- self::$info[] = "*** reverting $class";
- $start = microtime(true);
- // 打开输出缓冲区并获取内容
- ob_start();
- $migration = self::createMigration($class);
- if ($migration->down() !== false) {
- $time = microtime(true) - $start;
- self::$info[] = "*** reverted $class (time: " . sprintf('%.3f', $time) . "s)";
- ob_end_clean();
- return true;
- }
- ob_end_clean();
- $time = microtime(true) - $start;
- self::$info[] = "*** failed to revert $class (time: " . sprintf('%.3f', $time) . "s)";
- return false;
- }
- /**
- * @param $class
- * @return MigrationInterface
- * @throws InvalidConfigException
- */
- protected static function createMigration($class)
- {
- self::includeMigrationFile($class);
- /** @var MigrationInterface $migration */
- $migration = Yii::createObject($class);
- if ($migration instanceof BaseObject && $migration->canSetProperty('compact')) {
- $migration->compact = self::$compact;
- }
- return $migration;
- }
- /**
- * 包含给定迁移类名称的迁移文件
- *
- * @param $class
- */
- protected static function includeMigrationFile($class)
- {
- $class = trim($class, '\\');
- if (strpos($class, '\\') === false) {
- if (is_array(self::$migrationPath)) {
- foreach (self::$migrationPath as $path) {
- $file = $path . DIRECTORY_SEPARATOR . $class . '.php';
- if (is_file($file)) {
- require_once $file;
- break;
- }
- }
- } else {
- $file = self::$migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
- require_once $file;
- }
- }
- }
- /**
- * 获取数据迁移文件
- *
- * @return array
- */
- protected static function getNewMigrations()
- {
- $migrationPaths = [];
- if (is_array(self::$migrationPath)) {
- foreach (self::$migrationPath as $path) {
- $migrationPaths[] = [$path, ''];
- }
- } elseif (!empty(self::$migrationPath)) {
- $migrationPaths[] = [self::$migrationPath, ''];
- }
- foreach (self::$migrationNamespaces as $namespace) {
- $migrationPaths[] = [self::getNamespacePath($namespace), $namespace];
- }
- $migrations = [];
- foreach ($migrationPaths as $item) {
- list($migrationPath, $namespace) = $item;
- if (!file_exists($migrationPath)) {
- continue;
- }
- $handle = opendir($migrationPath);
- while (($file = readdir($handle)) !== false) {
- if ($file === '.' || $file === '..') {
- continue;
- }
- $path = $migrationPath . DIRECTORY_SEPARATOR . $file;
- if (preg_match('/^(m(\d{6}_?\d{6})\D.*?)\.php$/is', $file, $matches) && is_file($path)) {
- $class = $matches[1];
- if (!empty($namespace)) {
- $class = $namespace . '\\' . $class;
- }
- $time = str_replace('_', '', $matches[2]);
- $migrations[$time . '\\' . $class] = $class;
- }
- }
- closedir($handle);
- }
- ksort($migrations);
- return array_values($migrations);
- }
- /**
- * 根据命名空间获取路径
- *
- * @param $namespace
- * @return mixed
- */
- private static function getNamespacePath($namespace)
- {
- return str_replace('/', DIRECTORY_SEPARATOR, Yii::getAlias('@' . str_replace('\\', '/', $namespace)));
- }
- }
|