Вольно цитируя вступление к соответствующей статье на RailsGuides,
Миграции — это удобный способ управления структурой и изменениями схемы БД.
Конечно, можно вести дела по старинке, оперирую множеством SQL-файлов, или, о ужас!, редактируя куски SQL-кода в одном большом файле, который представляет собой актуальную схему БД.
Однако следить за этими изменениями, начиная с некоторого момента, становится очень сложно, не говоря уже о применении соответствующих изменений на продакшен-машине: тут нужно обладать ловкостью гепарда, силой медведя и мудростью всех восточных мудрецов, вместе взятых, чтобы все сделать правильно и ничего не уронить.
Но как быть, если Вы не обладаете какими-либо из вышеперечисленных качеств? Правильно, нужно систематизировать и автоматизировать процесс, переложив большую часть работы на машину.
Если Вы уже заинтересовались, или все еще не уверены, но перспектива иметь прозрачную историю изменений и возможность с помощью одной-двух консольных команд откатиться на любою версию схемы, звучит заманчиво, прошу под кат.
Начав работать с Ruby on Rails довольно быстро знакомишься с механизмом миграций, а уже через некоторое время не понимаешь, как можно вообще было обходиться без этого невероятно удобного инструмента.
Придя в проект, который разрабатывался на PHP, я постарался привнести в него хотя бы минимальный набор полезных инструментов, знакомых по опыту общения с Ruby on Rails. Одним из пунктов была система для поддержки миграций.
После некоторого поиска выбор был сделан в пользу Ruckusing Migrations, как наиболее похожего на то, что я видел в рельсах. Рапортую, что по прошествии более полугода полет нормальный!
Установка
Предполагается, что Вы используете Composer для управления зависимостями. Если нет, обязательно попробуйте, оно того стоит!
Также при желании Вы можете клонировать с GitHub репозиторий с примером: github.com/ArtemPyanykh/php_migrations_example
Для начала добавьте в свой composer.json:
"require" : {
"ruckusing/ruckusing-migrations": "dev-master"
}
и выполните
~/dev/php_migrations_example(master)$ php composer.phar install
Composer подтянет Ruckusing Migrations и установит в директорию vendors
.
Далее, я советую поступить следующим образом:
- Во-первых, создайте следующую структуру директорий:
. └── db ├── logs ├── migrations │ ├── main │ ├── php_migrations_example -> main/ │ └── php_migrations_example_test -> main/ └── utility
В директории
db
будут храниться Ваши миграции, конфиги и другие полезные штуки. Считайте этот каталог точкой отсчета для всего, что связано с миграциями.
Сами файлы с миграциями будут храниться вdb/migrations/main
.
php_migrations_example
иphp_migrations_example_test
— это названия девелоперской и тестовой баз данных.
Каталоги с соответствующими именами это просто символические ссылки на директориюmain
, так как вряд ли у Вас будут разные миграции для разных окружений. - Создайте файл
db/ruckus
со следующим содержимым:#!/bin/bash ruckus_dir="./../vendor/ruckusing/ruckusing-migrations" if [ ! -d $ruckus_dir ]; then echo "Ruckusing-Migrations wasn't detected. Please, use Composer to install the library." exit fi if [ ! -f "ruckusing.conf.php" ]; then echo "Ruckusing conf. file wasn't detected. Please, create proper ruckusing.conf.php." exit fi if [ "$#" -lt 1 ]; then echo "At least 1 argument required" echo "See ./ruckus --help" exit fi if [ "$1" == "--help" -o "$1" == "-h" ]; then echo "Usage: ./ruckus [--help] <task-name> [<parameter1>[ <parameter2> [...]]]" echo "" echo "The available ruckus commands are:" echo " ./ruckus db:migrate Ruckus scenario for tasks such as db:migrate, db:setup, etc." echo " ./ruckus db:generate <migration_name> Ruckus scenarion for generating migration scaffolding" echo "" exit fi php $ruckus_dir"/ruckus.php" "$1" "${@:2}"
Это просто небольшой скрипт, который я позволил себе написать, дабы упростить работу с Ruckusing Migrations.
- Наконец, создайте конфиг-файл
db/ruckusing.conf.php
:<?php //---------------------------- // DATABASE CONFIGURATION //---------------------------- /* Valid types (adapters) are Postgres & MySQL: 'type' must be one of: 'pgsql' or 'mysql' */ return array( 'db' => array( 'development' => array( 'type' => 'mysql', 'host' => 'localhost', 'port' => 3306, 'database' => 'php_migrations_example', 'user' => 'root', 'password' => 'root' ), 'test' => array( 'type' => 'mysql', 'host' => 'localhost', 'port' => 3306, 'database' => 'php_migrations_example_test', 'user' => 'root', 'password' => 'root' ) ), 'migrations_dir' => RUCKUSING_WORKING_BASE . '/migrations', 'db_dir' => RUCKUSING_WORKING_BASE . '/utility', 'log_dir' => RUCKUSING_WORKING_BASE . '/logs' ); ?>
Все, больше никаких настроек не требуется, Вы успешно интегрировали себе систему миграций!
Использование
В целом все очень просто. Давайте начнем с того, что сгенерируем миграцию:
~/dev/php_migrations_example(master)$ cd db
~/dev/php_migrations_example/db(master)$ ./ruckus db:generate CreateTestTable
Created migration: 20130508145210_CreateTestTable.php
Вы можете заметить, что в каталоге db/migrations/main после этого добавится файл примерно с таким же названием (timestamp будет другой) следующего содержания:
<?php
class CreateTestTable extends Ruckusing_Migration_Base
{
public function up()
{
}//up()
public function down()
{
}//down()
}
Миграции обладают тем свойством, что могут быть не только применены, но и отменены, если существует адекватный способ отката изменений. Именно такова семантика методов up() (применение изменений) и down() (откат изменений). Давайте создадим таблицу test с несколькими полями и парой индексов. Дополним файл следующим образом:
<?php
class CreateTestTable extends Ruckusing_Migration_Base
{
public function up()
{
$table = $this->create_table('test', array('options' => 'ENGINE=InnoDB DEFAULT CHARSET=utf8'));
$table->column('this', 'integer', array('unsigned' => true, 'null' => false, 'default' => '42'));
$table->column('that', 'string', array('limit' => '7'));
$table->column('those', 'datetime');
$table->finish();
$this->add_index('test', array('this', 'that'), array('unique' => true));
}//up()
public function down()
{
$this->drop_table('test');
}//down()
}
и запустим миграции:
~/dev/php_migrations_example/db(master)$ ./ruckus db:migrate
Started: 2013-05-08 7:05pm MSK
[db:migrate]:
Schema version table does not exist. Auto-creating.
Creating schema version table: schema_migrations
Migrating UP:
========= CreateTestTable ======== (0.31)
Finished: 2013-05-08 7:05pm MSK
Вы заметите, что помимо создания таблицы test, которая полностью соответствует описанной выше спецификации, также создалась таблица schema_migrations. Это нормально — именно здесь Ruckusing Migrations хранит информацию о том, какие миграции были применены, а какие не были.
Также просто можно откатиться или запустить миграции для другого окружения:
~/dev/php_migrations_example/db(master)$ ./ruckus db:migrate VERSION=-1
Started: 2013-05-08 7:13pm MSK
[db:migrate]:
Migrating DOWN:
========= CreateTestTable ======== (0.21)
Finished: 2013-05-08 7:13pm MSK
~/dev/php_migrations_example/db(master)$ ./ruckus db:migrate ENV=test
Started: 2013-05-08 7:14pm MSK
[db:migrate]:
Schema version table does not exist. Auto-creating.
Creating schema version table: schema_migrations
Migrating UP:
========= CreateTestTable ======== (0.24)
Finished: 2013-05-08 7:14pm MSK
Это лишь небольшая демонстрация возможностей системы. Однако, я думаю уже сейчас понятно, насколько проще и удобнее становится версионирование БД с применением правильного механизма миграций.
Замечания
- Если вы попробуете запустить скрипт
ruckus
не из директорииdb/
, то получите ошибку. Это связано с тем, что все пути в скрипте относительные, и при желании это легко исправляется. Нужно, однако, учесть один момент: по умолчанию, конфиг ищется в рабочей директории. - При применении миграций на продакшене нужно быть очень осторожным: если у Вас есть достаточно увесистая таблица, скажем в несколько гигабайт, и Вы примените к ней миграцию, которая каким-либо образом меняет схему, скорее всего будет беда. Хотя это, возможно, и не недостаток миграций как таковых, а скорее недостаток СУБД, тем не менее это несколько ограничивает возможности применения системы. Для обновления больших таблиц нужно использовать специализированные инструменты, например Percona Toolkit.
Ссылки
- Cтраница проекта на GitHub: github.com/ruckus/ruckusing-migrations
- Репозиторий с примером на GitHub: github.com/ArtemPyanykh/php_migrations_example
- Composer: getcomposer.org/
Автор: Deshene