Миграции
Внимание
Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!
Полное руководство по системе миграций в IO7 фреймворке.
Обзор
Система миграций IO7 позволяет:
- Создавать и изменять структуру БД версионируемым способом
- Откатывать изменения
- Заполнять данные через сидеры
- Генерировать тестовые данные через фабрики
Структура каталогов
project/
├── database/
│ ├── migrations/ # Файлы миграций
│ ├── seeders/ # Файлы сидеров
│ └── factories/ # Файлы фабрик
Пространство имён: IO\Database
DatabaseManager
Менеджер миграций, сидеров и фабрик. Все методы статические.
Файл: src/modules/io7/php/Database/DatabaseManager.php
Свойства DatabaseManager
| Свойство | Тип | Видимость | Описание | Значение по умолчанию |
|---|---|---|---|---|
$migrationsPath | string | private static | Путь к директории миграций | '' |
$seedersPath | string | private static | Путь к директории сидеров | '' |
$factoriesPath | string | private static | Путь к директории фабрик | '' |
init()
public static function init(string $basePath): void
Инициализировать пути к директориям миграций, сидеров и фабрик. Создаёт директории, если они не существуют.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$basePath | string | Базовый путь проекта | — | Да |
DatabaseManager::init('/path/to/project');
// Создаёт:
// /path/to/project/database/migrations/
// /path/to/project/database/seeders/
// /path/to/project/database/factories/
migrate()
public static function migrate(): int
Выполнить все миграции (в порядке сортировки файлов по имени).
Возвращает: int — количество выполненных миграций
$count = DatabaseManager::migrate();
echo "Выполнено миграций: {$count}\n";
rollback()
public static function rollback(int $steps = 1): int
Откатить последние N миграций (вызывает down() у каждой миграции в обратном порядке).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$steps | int | Количество миграций для отката | 1 | Нет |
Возвращает: int — количество откаченных миграций
// Откатить последнюю миграцию
DatabaseManager::rollback();
// Откатить последние 3 миграции
DatabaseManager::rollback(3);
seed()
public static function seed(?string $seederClass = null): int
Выполнить сидер(ы). Если класс не указан — выполняются все найденные сидеры.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$seederClass | ?\string | Имя класса сидера | null (все сидеры) | Нет |
Возвращает: int — количество выполненных сидеров
// Один сидер
DatabaseManager::seed(UserSeeder::class);
// Все сидеры
DatabaseManager::seed();
factory()
public static function factory(string $factoryClass, int $count = 1): array
Создать фабрику и сгенерировать данные. Автоматически создаёт экземпляр фабрики, устанавливает количество и вызывает make().
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$factoryClass | string | Имя класса фабрики | — | Да |
$count | int | Количество записей | 1 | Нет |
Возвращает: array — массив сгенерированных данных
// Генерация 10 записей
$users = DatabaseManager::factory(UserFactory::class, 10);
// Генерация 1 записи
$user = DatabaseManager::factory(UserFactory::class);
Migration
Абстрактный базовый класс миграции. Каждая миграция должна наследоваться от этого класса и реализовывать методы up() и down().
Файл: src/modules/io7/php/Database/Migration.php
up()
abstract public function up(): void
Выполнить миграцию (создание или изменение структуры БД). Этот метод вызывается при запуске migrate().
public function up(): void
{
$this->createTable('users', function (TableBuilder $table): void {
$table->bigIncrements('pkid');
$table->string('login', 50);
$table->string('name', 100);
$table->timestamps();
});
}
down()
abstract public function down(): void
Откатить миграцию (удаление или восстановление структуры). Этот метод вызывается при запуске rollback().
public function down(): void
{
$this->dropTable('users');
}
createTable()
protected function createTable(string $name, callable $callback): void
Создать новую таблицу. В callback передаётся экземпляр TableBuilder для определения полей.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя таблицы | — | Да |
$callback | callable | Callback с определением полей: function(TableBuilder $table): void | — | Да |
$this->createTable('users', function (TableBuilder $table): void {
$table->bigIncrements('pkid');
$table->string('login', 50);
$table->string('name', 100);
$table->string('email', 100)->nullable();
$table->string('password_hash', 255);
$table->boolean('active')->default(true);
$table->string('role', 20)->default('user');
$table->timestamps();
});
dropTable()
protected function dropTable(string $name): void
Удалить таблицу по имени.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя таблицы | — | Да |
$this->dropTable('users');
TableBuilder
Построитель таблиц для миграций. Используется внутри callback метода createTable().
Файл: src/modules/io7/php/Database/TableBuilder.php
Конструктор
public function __construct(string $name)
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя таблицы | — | Да |
Типы полей
bigIncrements()
public function bigIncrements(string $name): self
Auto-increment bigint (PRIMARY KEY).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->bigIncrements('pkid');
increments()
public function increments(string $name): self
Auto-increment int (PRIMARY KEY).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->increments('id');
integer()
public function integer(string $name): self
Integer поле.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->integer('age');
bigInteger()
public function bigInteger(string $name): self
Big Integer поле.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->bigInteger('user_id');
string()
public function string(string $name, int $length = 255): self
String поле.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$length | int | Максимальная длина | 255 | Нет |
$table->string('login', 50);
$table->string('name'); // длина 255 по умолчанию
$table->string('email', 100)->nullable();
text()
public function text(string $name): self
Text поле (длинный текст без ограничения длины).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->text('description');
boolean()
public function boolean(string $name): self
Boolean поле.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->boolean('active')->default(true);
decimal()
public function decimal(string $name, int $precision = 8, int $scale = 2): self
Decimal поле (число с плавающей точкой).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$precision | int | Общее количество цифр | 8 | Нет |
$scale | int | Количество цифр после точки | 2 | Нет |
$table->decimal('price', 10, 2); // 99999999.99
$table->decimal('total', 8, 2); // 999999.99
$table->decimal('weight'); // 999999.99 (по умолчанию)
date()
public function date(string $name): self
Date поле (дата без времени).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->date('birthday');
dateTime()
public function dateTime(string $name): self
DateTime поле (дата и время).
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->dateTime('published_at');
timestamps()
public function timestamps(): self
Добавляет поля created_at и updated_at.
$table->timestamps();
// Эквивалентно:
// $table->dateTime('created_at')->nullable();
// $table->dateTime('updated_at')->nullable();
json()
public function json(string $name): self
JSON поле.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$name | string | Имя поля | — | Да |
$table->json('metadata');
$table->json('settings')->nullable();
Модификаторы
default()
public function default($value): self
Установить значение по умолчанию для поля.
| Параметр | Тип | Описание | Значение по умолчанию | Обязателен |
|---|---|---|---|---|
$value | mixed | Значение по умолчанию | — | Да |
$table->boolean('active')->default(true);
$table->string('role', 20)->default('user');
$table->integer('status')->default(0);
$table->text('description')->default('');
nullable()
public function nullable(): self
Сделать поле nullable (разрешить NULL-значения).
$table->string('email', 100)->nullable();
$table->dateTime('deleted_at')->nullable();
$table->text('notes')->nullable();
Создание
create()
public function create(): void
Создать таблицу в БД. Вызывается автоматически после определения всех полей в callback createTable().
Примеры
Создание миграции
Файл: database/migrations/2024_01_15_000001_create_users_table.php
<?php
use IO\Database\Migration;
class CreateUsersTable extends Migration
{
public function up(): void
{
$this->createTable('users', function (TableBuilder $table): void {
$table->bigIncrements('pkid');
$table->string('login', 50);
$table->string('name', 100);
$table->string('email', 100)->nullable();
$table->string('password_hash', 255);
$table->boolean('active')->default(true);
$table->string('role', 20)->default('user');
$table->timestamps();
});
}
public function down(): void
{
$this->dropTable('users');
}
Создание таблицы с foreign key
<?php
use IO\Database\Migration;
class CreateOrdersTable extends Migration
{
public function up(): void
{
$this->createTable('orders', function (TableBuilder $table): void {
$table->bigIncrements('pkid');
$table->bigInteger('user_id');
$table->decimal('total', 10, 2);
$table->string('status', 20)->default('pending');
$table->json('items')->nullable();
$table->timestamps();
});
// Добавить индексы
\IO\Database\SqlConnection::connection()
->getSchemaBuilder()
->table('orders', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->index('user_id');
$table->index('status');
});
}
public function down(): void
{
$this->dropTable('users');
}
}
Создание таблицы с foreign key
<?php
use IO\Database\Migration;
class CreateOrdersTable extends Migration
{
public function up(): void
{
$this->createTable('orders', function (TableBuilder $table): void {
$table->bigIncrements('pkid');
$table->bigInteger('user_id');
$table->decimal('total', 10, 2);
$table->string('status', 20)->default('pending');
$table->json('items')->nullable();
$table->timestamps();
});
// Добавить индексы
\IO\Database\SqlConnection::connection()
->getSchemaBuilder()
->table('orders', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->index('user_id');
$table->index('status');
});
}
public function down(): void
{
$this->dropTable('orders');
}
}
Изменение существующей таблицы
<?php
use IO\Database\Migration;
use IO\Database\SqlConnection;
class AddPhoneToUsersTable extends Migration
{
public function up(): void
{
SqlConnection::connection()->getSchemaBuilder()
->table('users', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->string('phone', 20)->nullable()->after('email');
});
}
public function down(): void
{
SqlConnection::connection()->getSchemaBuilder()
->table('users', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->dropColumn('phone');
});
}
}
Таблица с JSON и timestamp
<?php
use IO\Database\Migration;
class CreateArticlesTable extends Migration
{
public function up(): void
{
$this->createTable('articles', function (TableBuilder $table): void {
$table->bigIncrements('pkid');
$table->bigInteger('author_id');
$table->string('title', 255);
$table->string('slug', 255);
$table->text('body');
$table->json('tags')->nullable();
$table->string('status', 20)->default('draft');
$table->dateTime('published_at')->nullable();
$table->timestamps();
});
\IO\Database\SqlConnection::connection()
->getSchemaBuilder()
->table('articles', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->index('author_id');
$table->index('status');
$table->unique('slug');
});
}
public function down(): void
{
$this->dropTable('articles');
}
}
Запуск миграций
use IO\Database\DatabaseManager;
// Инициализация
DatabaseManager::init('/path/to/project');
// Выполнить все миграции
$count = DatabaseManager::migrate();
echo "Выполнено: {$count}\n";
// Откатить последние 3
DatabaseManager::rollback(3);
// Выполнить сидер
DatabaseManager::seed(UserSeeder::class);
// Сгенерировать данные
$users = DatabaseManager::factory(UserFactory::class, 100);
Консольные команды
# Выполнить все миграции
php iocli migrate
# Откатить миграции
php iocli migrate:rollback
# Статус миграций
php iocli migrate:status
# Свежая миграция (drop + migrate)
php iocli migrate:fresh
# Выполнить сидер
php iocli db:seed
# Создать новую миграцию
php iocli make:migration CreateUsersTable
# Создать сидер
php iocli make:seeder UserSeeder
# Создать фабрику
php iocli make:factory UserFactory
Рекомендации
- Именование файлов:
YYYY_MM_DD_HHMMSS_descriptive_name.php - Каждая миграция должна быть обратима — реализуйте
down() - Тестируйте миграции на dev-окружении перед продакшеном
- Используйте сидеры для начальных данных (админы, настройки)
- Используйте фабрики для тестовых данных
- Изменяйте структуру постепенно — не меняйте типы существующих полей
- Добавляйте индексы для полей, по которым часто выполняется поиск
- Используйте
bigIncrements('pkid')для первичных ключей
FAQ
Как проверить статус миграций?
$files = glob(DatabaseManager::getMigrationsPath() . '/*.php');
sort($files);
foreach ($files as $file) {
echo basename($file) . "\n";
}
Что делать при ошибке миграции?
- Исправьте код миграции
- Если таблица уже создана — выполните
down()вручную - Запустите
migrate()снова
Как переименовать поле?
public function up(): void
{
// PostgreSQL
SqlConnection::connection()->getSchemaBuilder()
->table('users', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->renameColumn('old_name', 'new_name');
});
}
Как добавить индекс?
public function up(): void
{
SqlConnection::connection()->getSchemaBuilder()
->table('users', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->index('email');
$table->unique('login');
});
}
Как выполнить миграцию в production?
php iocli migrate
Убедитесь, что бэкап БД создан перед запуском.
Как использовать разные подключения для миграций?
// Через named connection
SqlConnection::connection('mysql_replica')->getSchemaBuilder()
->table('users', function (\Illuminate\Database\Schema\Blueprint $table): void {
$table->index('email');
});
TableBuilder — методы
Типы полей
| Метод | SQL тип | Описание |
|---|---|---|
id($name) | BIGINT AUTO_INCREMENT | Алиас для bigIncrements |
increments($name) | INT AUTO_INCREMENT | |
bigIncrements($name) | BIGINT AUTO_INCREMENT | |
smallIncrements($name) | SMALLINT AUTO_INCREMENT | |
tinyIncrements($name) | TINYINT AUTO_INCREMENT | |
integer($name) | INT | |
unsignedInteger($name) | INT UNSIGNED | |
bigInteger($name) | BIGINT | |
unsignedBigInteger($name) | BIGINT UNSIGNED | |
smallInteger($name) | SMALLINT | |
unsignedSmallInteger($name) | SMALLINT UNSIGNED | |
tinyInteger($name) | TINYINT | |
unsignedTinyInteger($name) | TINYINT UNSIGNED | |
mediumInteger($name) | MEDIUMINT | |
unsignedMediumInteger($name) | MEDIUMINT UNSIGNED | |
string($name, $len) | VARCHAR($len) | По умолчанию 255 |
char($name, $len) | CHAR($len) | По умолчанию 4 |
text($name) | TEXT | |
mediumText($name) | MEDIUMTEXT | |
longText($name) | LONGTEXT | |
boolean($name) | TINYINT(1) | |
decimal($name, $p, $s) | DECIMAL($p,$s) | precision=8, scale=2 |
unsignedDecimal($name, $p, $s) | DECIMAL UNSIGNED | |
double($name, $p, $s) | DOUBLE | |
float($name, $p) | FLOAT | |
date($name) | DATE | |
dateTime($name) | DATETIME | |
dateTimeTz($name) | DATETIME | |
timestamp($name) | TIMESTAMP | |
timestampTz($name) | TIMESTAMP | |
timestamps() | TIMESTAMP x2 | created_at + updated_at |
timestampsTz() | TIMESTAMP x2 | с timezone |
softDeletes($name) | TIMESTAMP | deleted_at |
time($name) | TIME | |
timeTz($name) | TIME | |
year($name) | YEAR | |
json($name) | JSON | |
jsonb($name) | JSON | |
binary($name) | BLOB | |
uuid($name) | CHAR(36) | |
ulid($name) | CHAR(26) | |
enum($name, $arr) | ENUM | |
set($name, $arr) | SET | |
ipAddress($name) | VARCHAR(45) | |
macAddress($name) | VARCHAR(17) |
Morphs
| Метод | Создаёт |
|---|---|
morphs($name) | {name}_type VARCHAR + {name}_id BIGINT UNSIGNED |
nullableMorphs($name) | nullable версия |
uuidMorphs($name) | UUID версия |
Foreign
| Метод | Описание |
|---|---|
foreignId($name) | BIGINT UNSIGNED |
foreignIdFor($name) | BIGINT UNSIGNED |
foreignUlid($name) | ULID |
constrained($table, $column) | Foreign key constraint |
cascadeOnDelete() | ON DELETE CASCADE |
restrictOnDelete() | ON DELETE RESTRICT |
nullOnDelete() | ON DELETE SET NULL |
cascadeOnUpdate() | ON UPDATE CASCADE |
Индексы
| Метод | Описание |
|---|---|
primary($columns) | PRIMARY KEY |
unique() | UNIQUE (цепочка) |
index() | INDEX (цепочка) |
fulltext() | FULLTEXT |
spatialIndex() | SPATIAL |
Модификаторы (цепочка)
| Метод | Описание |
|---|---|
after($col) | Разместить после поля |
first() | Разместить первым |
default($value) | DEFAULT |
nullable() | NULL |
unsigned() | UNSIGNED |
useCurrent() | DEFAULT CURRENT_TIMESTAMP |
useCurrentOnUpdate() | ON UPDATE CURRENT_TIMESTAMP |
comment($text) | COMMENT |
storedAs($expr) | GENERATED ALWAYS AS ... STORED |
virtualAs($expr) | GENERATED ALWAYS AS ... VIRTUAL |
generatedAs($expr) | GENERATED ALWAYS AS ... |
always() | ALWAYS (для generated) |
invisible() | INVISIBLE |
autoIncrement() | AUTO_INCREMENT |
Миграции модулей
Миграции могут быть в каждом модуле. iocli migrate автоматически сканирует:
app/Migrations/(проект)modules/*/Migrations/(из зарегистрированных модулей вapp/Module.php)
Структура
app/Migrations/ ← App\Migrations namespace
001_CreateUsersTable.php
modules/io7/php/Migrations/ ← IO\Migrations namespace
CreateCronTable.php
CreateCronDataTable.php
CreateCronWorkTable.php
CreateCacheTable.php
CreateMigrationsTable.php
SeedCronTable.php
modules/crm/Migrations/ ← Crm\Migrations namespace (если модуль crm)
CreateRolesTable.php
Регистрация модулей
Модули регистрируются в app/Module.php:
public static function create_app(): \IO\App
{
$app = new \IO\App();
$app->add_modules([
\IO\Module::class, // modules/io7/php/ → IO\
\App\Module::class, // app/ → App\
\Crm\Module::class, // modules/crm/ → Crm\
]);
$app->init();
return $app;
}
Команды
php iocli migrate # Миграции всех модулей
php iocli migrate:status # Статус всех миграций
php iocli migrate:rollback # Откат последнего batch
php iocli app:init # Инициализация (создание таблиц + seed)
Migration класс — методы
| Метод | Описание |
|---|---|
createTable($name, $callback) | Создать таблицу |
dropTable($name) | Удалить таблицу |
hasTable($name) | Проверить существование таблицы (аналог Schema::hasTable()) |
createTableIfNotExists($name, $callback) | Создать таблицу если не существует |
hasTable
if (self::hasTable('migrations')) return;
Проверяет SHOW TABLES LIKE 'table_name'. Возвращает bool.
createTableIfNotExists
$this->createTableIfNotExists('users', function ($table) {
$table->bigIncrements('pkid');
$table->string('name');
});
Если таблица уже существует — пропускает. Полезно для базовых миграций.
Миграции в модулях
Миграции могут быть в каждом модуле. iocli migrate автоматически сканирует:
app/Migrations/(проект)modules/*/Migrations/(из зарегистрированных модулей)
Структура:
app/Migrations/ ← App\Migrations
2026_06_26_142056_create_roles_table.php
modules/io7/php/Migrations/ ← IO\Migrations
0000_00_00_000001_create_migrations_table.php
0000_00_00_000002_create_cache_table.php
0000_00_00_000003_create_cron_table.php
0000_00_00_000004_seed_cron_table.php
0000_00_00_000005_create_cron_data_table.php
0000_00_00_000006_create_cron_work_table.php
CLI команды
php iocli migrate # Выполнить миграции
php iocli migrate:status # Статус (если таблицы migrations нет — предупреждение)
php iocli migrate:rollback # Откат последнего batch
php iocli migrate:fresh # Удалить все таблицы + миграции заново
php iocli app:init # Инициализация проекта
Статические методы миграций
| Метод | Аналог Laravel | Описание |
|---|---|---|
hasTable($table) | Schema::hasTable() | Проверить существование таблицы |
hasIndex($table, $indexName) | Schema::hasIndex() | Проверить существование индекса |
hasColumn($table, $column) | Schema::hasColumn() | Проверить существование колонки |
if (self::hasTable('migrations')) return;
if (!self::hasColumn('users', 'avatar')) { /* add column */ }
if (!self::hasIndex('users', 'idx_email')) { /* add index */ }
updateTable
Модифицировать существующую таблицу (аналог Schema::table()).
public function up(): void
{
$this->updateTable('users', function ($table) {
$table->string('avatar', 255)->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
updateTable() использует Schema::table() с Blueprint. TableBuilder::applyTo() применяет колонки к Blueprint.
dropTable / renameTable
// Удалить таблицу
$this->dropTable('old_users');
// Переименовать таблицу
$this->renameTable('users', 'accounts');
updateTable — удаление и переименование столбцов
public function up(): void
{
$this->updateTable('users', function ($table) {
// Добавить столбец
$table->string('avatar')->nullable();
// Удалить столбец
$table->dropColumn('legacy_field');
// Удалить несколько столбцов
$table->dropColumns(['old_col1', 'old_col2']);
// Переименовать столбец
$table->renameColumn('old_name', 'new_name');
});
}
renameTable
public function up(): void
{
$this->renameTable('users', 'accounts');
}
public function down(): void
{
$this->renameTable('accounts', 'users');
}