Миграции являются удобным инструментом для изменения структуры базы данных и поддержания ее в актуальном состоянии.
Yii2 поддерживает миграции из коробки. Использование миграций подробно описано в документации. Управление миграциями осуществляется из командной строки.
В статье описывается использование миграций для управления несколькими схемами БД. Как оказалось, это совсем несложно реализовать, но почему-то не нашел ни одного описания.
Нередко мне встречаются проекты, где используется более одной базы данных. В этом случае хочется управлять каждой базой в отдельности. Как вариант, можно в каждой миграции указывать, к какой базе данных ее применять, или явно указывать бд в каждом запросе. Но это не очень удобно и есть большая вероятность случайно накатить миграцию не на ту базу. Кроме того, таблица миграций все равно будет только в основной базе.
К сожалению, готового решения не нашел, поэтому заглянем в код фреймворка и посмотрим, как работают миграции.
Команда yii migrate использует yii\console\controllers\MigrateController
Для создания новой миграции используется шаблон migration.php
Все миграции наследуются от yii\db\Migration.
1. Для начала переопределяем базовый класс миграции для каждой БД. В папке components создаем классы CustomMigrationDb и CustomMigrationDb2
CustomMigrationDb2.php
2. В папке \views\migrations создаем файлы migration_db.php и migration_db2.php — шаблоны для новых миграций. Достаточно скопировать базовый шаблон и поменять базовый класс на те, что мы только что создали.
migration_db2.php
3. Теперь создаем контроллеры. В папке commands создаем классы MigrateDbController и MigrateDb2Controller. Наследуем их от yii\console\controller\MigrateController.
MigrateDb2Controller.php
Здесь указываем базу данных, в которой будет создана таблица с миграциями, шаблон для создания новых миграций и путь до папки с миграциями.
4. Отключаем стандартный yii migrate, вместо него будем использовать один из созданных контроллеров. Для этого добавим в файл \config\console.php следующий код:
UPD: zvirusz предлагает все контроллеры миграций вынести в controllerMap. Для этого нужно добавить в \config\console.php такой код.
В результате получаем независимые миграции для каждой БД. Миграции запускаются командами
Работают как обычные миграции. Новые миграции создаются в отдельных папках для каждой бд командами
Написал небольшое приложение для демонстрации. Выложил на github.
Спасибо за внимание.
Yii2 поддерживает миграции из коробки. Использование миграций подробно описано в документации. Управление миграциями осуществляется из командной строки.
В статье описывается использование миграций для управления несколькими схемами БД. Как оказалось, это совсем несложно реализовать, но почему-то не нашел ни одного описания.
Нередко мне встречаются проекты, где используется более одной базы данных. В этом случае хочется управлять каждой базой в отдельности. Как вариант, можно в каждой миграции указывать, к какой базе данных ее применять, или явно указывать бд в каждом запросе. Но это не очень удобно и есть большая вероятность случайно накатить миграцию не на ту базу. Кроме того, таблица миграций все равно будет только в основной базе.
К сожалению, готового решения не нашел, поэтому заглянем в код фреймворка и посмотрим, как работают миграции.
Команда yii migrate использует yii\console\controllers\MigrateController
Для создания новой миграции используется шаблон migration.php
Все миграции наследуются от yii\db\Migration.
1. Для начала переопределяем базовый класс миграции для каждой БД. В папке components создаем классы CustomMigrationDb и CustomMigrationDb2
CustomMigrationDb2.php
<?php
namespace app\components;
use yii\db\Migration;
class CustomMigrationDb2 extends Migration{
public function init()
{
$this->db = 'db2';
parent::init();
}
}
2. В папке \views\migrations создаем файлы migration_db.php и migration_db2.php — шаблоны для новых миграций. Достаточно скопировать базовый шаблон и поменять базовый класс на те, что мы только что создали.
migration_db2.php
<?php
/**
* This view is used by console/controllers/MigrateController.php
* The following variables are available in this view:
*/
/* @var $className string the new migration class name */
echo "<?php\n";
?>
use yii\db\Schema;
use app\components\CustomMigrationDb2;
class <?= $className ?> extends CustomMigrationDb2
{
public function up()
{
}
public function down()
{
echo "<?= $className ?> cannot be reverted.\n";
return false;
}
/*
// Use safeUp/safeDown to run migration code within a transaction
public function safeUp()
{
}
public function safeDown()
{
}
*/
}
3. Теперь создаем контроллеры. В папке commands создаем классы MigrateDbController и MigrateDb2Controller. Наследуем их от yii\console\controller\MigrateController.
MigrateDb2Controller.php
<?php
namespace app\commands;
use yii\console\controllers\MigrateController;
class MigrateDb2Controller extends MigrateController
{
public $db = 'db2';
public $templateFile = '@app/views/migrations/migration_db2.php';
public $migrationPath = '@app/migrations/db2';
}
Здесь указываем базу данных, в которой будет создана таблица с миграциями, шаблон для создания новых миграций и путь до папки с миграциями.
4. Отключаем стандартный yii migrate, вместо него будем использовать один из созданных контроллеров. Для этого добавим в файл \config\console.php следующий код:
'controllerMap' => [
...
'migrate' => [
'class' => 'app\commands\MigrateDbController',
],
],
UPD: zvirusz предлагает все контроллеры миграций вынести в controllerMap. Для этого нужно добавить в \config\console.php такой код.
'controllerMap' => [
...
'migrate' => [ // Fixture generation command line.
'class' => 'yii\console\controllers\MigrateController',
'db' => 'db',
'templateFile' => '@app/views/migrations/migration_db.php',
'migrationPath' => '@app/migrations/db'
],
'migrate-db' => [ // Fixture generation command line.
'class' => 'yii\console\controllers\MigrateController',
'db' => 'db',
'templateFile' => '@app/views/migrations/migration_db.php',
'migrationPath' => '@app/migrations/db'
],
'migrate-db2' => [ // Fixture generation command line.
'class' => 'yii\console\controllers\MigrateController',
'db' => 'db2',
'templateFile' => '@app/views/migrations/migration_db2.php',
'migrationPath' => '@app/migrations/db2'
],
],
В результате получаем независимые миграции для каждой БД. Миграции запускаются командами
yii migrate-db
yii migrate-db2
Работают как обычные миграции. Новые миграции создаются в отдельных папках для каждой бд командами
yii migrate-db/create и
yii migrate-db2/create
Написал небольшое приложение для демонстрации. Выложил на github.
Спасибо за внимание.
DenisOgr
А зачем ради одно строки — писать контроллеры, темплейты? У нас в проекте несколько БД. В нужных миграциях просто переопределям нужное проперти указывающее коннект к БД. Все!
antonshell
Хотелось чтобы в каждой базе была своя таблица migrate. Хотелось, чтобы у каждой БД было свое состояние. Хотелось, чтобы миграции для каждой базы данных хранились отдельно.
Например нужно откатить последние 2 миграции для db2, Но после них уже было создано несколько миграций к db1. Их откатывать не нужно. Или нужно обновить только db1, а db2 не обновлять.
Ну, особо много кода писать не пришлось. Конечно, дублирование есть. Шаблоны копировать ради одной строчки наверное не стоило. Но это самое простое решение. И не создает особых проблем.
DenisOgr
хм… а зачем откатывать последние две миграции от одной БД?
а что если смотреть на БД как на атомарное хранилище, и не нужно его разделять…
приведите пример такой задачи/проблемы. ну например, на лайфе нужно откатить последние миграции (которые удаляют таблицу с пользователями) (конец сарказма). серьезно, можно пример?
в процессе разработки вы увидели что накатиоли миграцию не в ту БД. зашли поправили. явно до лайфа/по мержа-до ревью кода такая миграция не доживет.
за все время что я писал миграции — ни разу такого не было… да и а что если кто то удалить вообще базу данных/таблицу/записи из таблицы миграцией? у вас есть защита от этого?
antonshell
Мне кажется, все зависит от проекта. И от того, кому как удобнее. Это 2 разных подхода. Я лишь предложил свое решение.
Одна БД — атомарное хранилище. А если 2 и более, все вместе это будет атомарное хранилище? В любом случае? Если в проекте несколько БД, то наверное на то есть свои причины.
Зачем сарказм? Это и был пример(кажется, недостаточно удачный). Естественно, мне едва ли придется так делать. Вообще не так уж часто приходится откатывать миграции.
Я думал только об удобстве использования. Мне удобнее, когда миграции для каждой бд лежат в своей папке. И если я обновляю db1, то я точно знаю, что обновляю именно db1.
Специально погуглил и нашел несколько примеров, не связанных с yii, где миграции все-таки разделены:
http://samokhvalov.info/blog/all/phinx-multiple-databases/
https://github.com/robmorgan/phinx/issues/180
Еще 2 примера, правда для Ruby. Я не работал с Ruby, но, если я правильно понял, здесь также предлагают разделение.
http://techvomit.net/multiple-dbs-with-active-record-sans-rails/
http://geekhmer.github.io/blog/2015/02/07/ruby-on-rails-connect-to-multiple-databases-and-migrations/
pewpew
Миграции, они ведь не только для БД. Ими можно решать такие задачи, как заполнение файлов в папке /upload/, например или переименовывание уже загруженных файлов. Поэтому логично использовать общую таблицу migrate и общее состояние.
antonshell
Интересно, не встречал такого. Но не совсем понятно, как это влияет на общее состояние
Наверное это зависит от проекта. Если 2 базы данных действительно сильно связаны, то лучше все миграции вместе хранить и накатывать. Разумеется, я сначала рассматривал этот вариант. Но в итоге я решил, что управлять БД по отдельности будет удобнее.
Мне кажется, тут вопрос действительно больше в удобстве.