Простые миграции с PHPixie Migrate
Запуск консольного расширения несколько недель назад позволил гораздо расширить спектр задач решаемых с помощью PHPixie и её компонентов. И теперь я рад представить вам PHPixie Migrate — утилиту для миграции баз данных. Как и другие компоненты она может работать полностью самостоятельно, и в конце статьт я приведу пример того как запустить её без фреймворка.
1. Обновить конфиг соединения с БД (database.php)
PHPixie теперь поддерживает альтернативный синтаксис где вместо одной строки соединения используются дополнительные параметры, например:
Вместо старого:
return array(
'default' => array(
'connection' => 'mysql:dbname=phpixie',
'user' => 'phpixie',
'password' => 'phpixie',
'driver' => 'pdo'
)
);
теперь используется
return array(
'default' => array(
'database' => 'phpixie',
'user' => 'phpixie',
'password' => 'phpixie',
'adapter' => 'mysql', // one of: mysql, pgsql, sqlite
'driver' => 'pdo'
)
);
PHPixie и дальше будет поддерживать старую конфигурацию для запросов, но для работы Migrate важно чтобы конфиг был в новом виде.
2. Скопировать папку /assets/migrate и конфиг файл /assets/config/migrate.php со скелета в свой проект.
Вот и все.
Конфигурация
Рассмотрим конфиг /assets/config/migrate.php:
array(
'default' => array(
// имя соединения с database.php
'connection' => 'default',
// путь в котором хранятся миграции, относительно папки /assets/migrate/
'path' => 'migrations',
// не обязательно:
// имя таблицы в которой хранить миграции
'migrationTable' => '__migrate',
// имя поля в таблице миграций
'lastMigrationField' => 'lastMigration'
)
),
// настройки сидирования (об этом позже)
'seeds' => array(
'default' => array(
// имя соединения с database.php
'connection' => 'default',
// путь в котором хранятся сиды, относительно папки /assets/migrate/
'path' => 'seeds'
)
)
);
Скорее всего вам не понадобится изменять ничего в этом конфиге, разве что у вас две и больше баз, или вам нужны разные сиды для той же БД.
Создание и удаление базы
Создавать и удалять базу теперь можно прямо с консоли, это делает новая команда framework: database:
framework:database ACTION [ CONFIG ]
Create or drop a database
Arguments:
ACTION Either 'create' or 'drop'
CONFIG Migration configuration name, defaults to 'default'
То есть console framework: database create проверит существует ли база, и если нет то создаст ее, а console framework: database drop удалит ее.
Миграции
Ну, а теперь о самом главном. Сначала короткое вступление для тех кто ничего подобного пока не использовал.
Миграции дают возможность хранить изменения структуры базы в коде что гораздо удобнее чем перебрасываться готовыми дампами, а затем вручную изменять базу на продакшене. Принцип работы прост: в базе хранится имя последней миграции и при запуске команды будут применены все миграции которые «больше» ее в порядке natsort (), то есть если у нас есть файлы 1.sql, 2.sql… 22.sql, a последняя в базе 13.sql то выполнятся все от 14 до 22, и затем в базе сохранится 22 как имя последней. Они могут быть в формате .sql или .php.
SQL миграции
Тут все просто, это просто SQL файл в котором отдельные выражения разделяются сепаратором »-- statement», например:
CREATE TABLE fairies(
id int NOT NULL,
name VARCHAR(255)
);
-- statement
CREATE TABLE flowers(
id int NOT NULL,
name VARCHAR(255)
);
PHP миграции
Это просто PHP файл с возможностью выполнения запросов и даже доступа к запросам PHPixie Database:
$this->execute("CREATE TABLE fairies(
id int NOT NULL,
name VARCHAR(255)
)");
$this->message("Какое-то сообщение в консоль");
// привычные запросы
$this->connection()->updateQuery()
->table('users')
->set(['role' => 'user'])
->execute();
Кстати очень рекомендую в именах миграций писать краткое описание, а не просто цифры. Поскольку используется порядок natsort то можно смело писать комент после знака _, например 33_fairies_table.sql
Тут сразу стоит ответить на 2 вопроса:
Почему нет down миграций для отката:
Если думать с точки зрения самой БД, то понятия отката нет как такого. Откат это просто еще одна миграция вперед, которая отменяет то что сделали предыдущие. К тому же такой откат не всегда даже возможен, так как если вы в одной миграции удалили таблицу, то откат мог бы ее воссоздать, но уж никак не восстановить данные.
Почему изменения делаются сырыми SQL запросами, а не универсальными методами типа createTable () ?
Проблема универсальных методов в том что они упускают тонкости в различиях разных баз, и много вещей приходится угадывать. К тому же возможен вариант когда библиотек обновится и начнет создавать поля и таблицы чуть по другому, и тогда продакшн база со старыми миграциями будет отличатся от новой где были запущены те же миграции несколько месяцев спустя. Плюс существует уже и так столько графических утилит для создания SQL для таблиц и конвертации с одной БД в другую, что делать хелпер методы для этого кажется довольно лишне.
Сиды
Сиды — это данные которыми можно наполнить базу. Например это могут быть какие-то дефолтные пользователи, категории товаров итд, также их можно использовать для наполнения базы тестовыми данными для функциональных тестов. Имя файла должно совпадать с именем таблицы, доступные форматы .php и .json. Например:
// /assets/migrate/seeds/fairies.php
1,
'name' => 'Pixie'
),
array(
'id' => 2,
'name' => 'Trixie'
),
);
// /assets/migrate/seeds/flowers.json
[
{
"id": 1,
"name": "daisy"
},
{
"id": 2,
"name": "Rose"
},
]
В случае с .php кроме возвращения массива данных также есть возможность сделать все руками используя соединение с БД:
// /assets/migrate/seeds/fairies.php
connection()->insertQuery()
->data([
'id' => 1,
'name' => 'Pixie'
])
->execute();
Для вставки сидов используется команда framework: seed:
framework:seed [ --truncate ] [ CONFIG ]
Seed the database with data
Options:
truncate Truncate the tables before inserting the data.
Arguments:
CONFIG Seed configuration name, defaults to 'default'
Если в таблице уже существуют данные то это приведет к выводу ошибки. Для того чтобы очистить таблицу перед вставкой можно использовать параметр --truncate.
Очевидно что для одного и того же соединения с БД можно задать несколько профилей сидов в файле конфигурации.
Использование без фреймворка
Как и все другие компоненты PHPixie Migrate можно использовать отдельно от фреймворка, примерно вот так:
$slice = new \PHPixie\Slice();
$database = new \PHPixie\Database($slice->arrayData(array(
'default' => array(
'database' => 'phpixie',
'user' => 'phpixie',
'password' => 'phpixie',
'adapter' => 'mysql', // one of: mysql, pgsql, sqlite
'driver' => 'pdo'
)
)));
$filesystem = new \PHPixie\Filesystem();
$migrate = new \PHPixie\Migrate(
$filesystem->root(__DIR__.'/assets/migrate'),
$database,
$slice->arrayData(array(
'migrations' => array(
'default' => array(
'connection' => 'default',
'path' => 'migrations',
)
),
'seeds' => array(
'default' => array(
'connection' => 'default',
'path' => 'seeds'
)
)
)));
$cli = new \PHPixie\CLI();
$console = new \PHPixie\Console($slice, $cli, $migrate->consoleCommands());
$console->runCommand();
Здесь имена команд будут run, seed, database без префикса framework.
Есть конечно еще несколько фич которые хотелось бы добавить, но надеюсь компонент понравится тем пользователям которые уже заждались миграций для 3-й PHPixie.