База данных: Миграции
Миграции — это как система контроля версий для вашей базы данных. Представьте, что вы работаете над проектом в команде и каждый разработчик меняет структуру базы данных по-своему (кто-то добавил колонку, кто-то переименовал таблицу). Через неделю никто не помнит, что и когда менялось. Миграции решают эту проблему — каждая миграция это как коммит в git, но для схемы базы данных.
Миграции позволяют:
- Синхронизировать схему БД между разработчиками
- Версионировать изменения структуры
- Откатывать изменения, если что-то пошло не так
- Обновлять базу на продакшене без потери данных
Конфигурирование
По умолчанию Imhotep использует таблицу migrations для отслеживания выполненных миграций:
'migrations' => [
'table' => 'migrations',
'connection' => null, // Подключение для миграций (null = default)
],
Создание миграций
Для создания миграции, воспользуйтесь консольной командой:
php imhotep make:migration create_users_table
Команда создаст файл в папке database/migrations с именем, содержащим текущую дату и время:
database/migrations/
└── 2026_08_05_103245_create_users_table.php
Именование миграций
Imhotep определяет тип операции по имени миграции:
| Имя миграции | Что делает |
|---|---|
| create_users_table | Создание таблицы users |
| add_role_to_users_table | Добавление колонки role |
| drop_role_from_users_table | Удаляет колонку role из таблицы users |
| rename_role_column | Переименовывает колонку (если указана таблица) |
Совет: Давайте миграциям осмысленные имена. Через месяц вы будете благодарны себе, когда увидите в списке add_soft_deletes_to_users_table вместо update_users.
Структура миграции
Пример миграции с дополнительными полезными свойствами, которые не обязательно указывать.
<?php
use Imhotep\Database\Migrations\Migration;
use Imhotep\Database\Schema\Blueprint;
return new class extends Migration
{
// Какое подключение к БД использовать (по умолчанию текущее)
public ?string $connection = 'mysql';
// Выполнять миграцию в транзакции (по умолчанию true)
public bool $useTransaction = true;
// Код, который применяет изменения
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
}
// Код, который откатывает изменения
public function down(): void
{
Schema::dropIfExists('posts');
}
};
- Метод
up()— выполняется при применении миграции. Здесь вы создаете таблицы, добавляете колонки, индексы. - Метод
down()— выполняется при откате. Здесь вы отменяете изменения, сделанные вup().
Управление миграциями
Ниже предоставлен список основных команд, которые вам понадобятся:
# Применить все ожидающие миграции
php console migrate
# Показать, что будет выполнено (без реальных изменений)
php console migrate --pretend
# Увеличить batch для каждой миграции (полезно для CI/CD)
php console migrate --step
# Применить конкретную миграцию по имени файла
php console migrate --name=create_users_table
# Откатить последнюю миграцию
php console migrate:rollback
# Откатить указанное количество миграций
php console migrate:rollback --step=3
# Откатить все миграции
php console migrate:reset
# Откатить и применить заново (сброс)
php console migrate:refresh
# Показать статус всех миграций
php console migrate:status
# Создать таблицу миграций (если не создана)
php console migrate:install
Работа с базами данных
Фасад Schema позволяет создавать и удалять базы данных. Это удобно для настройки окружений, тестирования и автоматизации развертывания.
Некоторые СУБД (например, SQLite) используют файлы вместо классических баз данных, поэтому методы createDatabase и dropDatabase работают с файловой системой.
use Imhotep\Facades\Schema;
// Создание базы данных
Schema::createDatabase('my_database');
// Удаление базы данных
Schema::dropDatabase('my_database');
// Безопасное удаление (проверка существования)
Schema::dropDatabaseIfExists('my_database');
// Получение списка таблиц
Schema::getTables();
Работа с таблицами
Таблицы — основа любой базы данных. Фасад Schema предоставляет методы для создания, изменения, переименования и удаления таблиц.
Создание таблицы
use Imhotep\Database\Schema\Table;
use Imhotep\Facades\Schema;
Schema::create('users', function (Table $table) {
$table->id();
$table->string('name', 60);
$table->timestamps();
});
Изменение таблицы
use Imhotep\Database\Schema\Table;
use Imhotep\Facades\Schema;
Schema::table('table_name', function (Table $table) {
// Добавление новых колонок
$table->string('phone', 20)->nullable();
$table->timestamp('last_login_at')->nullable();
// Изменение колонок
$table->string('name', 255)->change();
// Переименование колонок
$table->renameColumn('old_name', 'new_name');
});
Переименование таблицы
use Imhotep\Facades\Schema;
Schema::rename('old_users', 'new_users');
Удаление таблиц
use Imhotep\Facades\Schema;
// Безусловное удаление
Schema::drop('users');
// Безопасное удаление (только если существует)
Schema::dropIfExists('users');
// Удаление всех таблиц в базе данных
Schema::dropAllTables();
Работа со столбцами
После создания таблицы часто требуется добавлять, изменять или удалять отдельные столбцы. Фасад Schema позволяет управлять структурой колонок без полной перестройки таблицы.
Проверка существования
use Imhotep\Facades\Schema;
// Проверка одной колонки
if (Schema::hasColumn('users', 'email')) {
// Колонка email существует
}
// Проверка нескольких колонок
if (Schema::hasColumns('users', ['email', 'phone'])) {
// Все колонки существуют
}
Получение информации
use Imhotep\Facades\Schema;
// Список всех колонок
$columns = Schema::getColumns('users');
// Тип колонки
$type = Schema::getColumnType('users', 'email');
echo "Тип: {$type}\n";
Переименование столбца
use Imhotep\Facades\Schema;
Schema::renameColumn('old_column', 'new_column');
Удаление столбцов
use Imhotep\Facades\Schema;
Schema::dropColumn('column');
Schema::dropColumns('table', ['column1', 'column2']);
Типы столбцов
При создании таблицы нужно указать тип данных для каждой колонки. Imhotep поддерживает все основные типы SQL, адаптированные под конкретную СУБД.
Автоинкремент
$table->id(); // bigIncrements('id')
$table->bigIncrements('user_id'); // BIGINT UNSIGNED AUTO_INCREMENT
$table->increments('item_id'); // INT UNSIGNED AUTO_INCREMENT
$table->smallIncrements('category_id'); // SMALLINT UNSIGNED AUTO_INCREMENT
Строковые типы
$table->string('name', 100); // VARCHAR(100)
$table->char('code', 10); // CHAR(10)
$table->text('content'); // TEXT
$table->mediumText('description'); // MEDIUMTEXT
$table->longText('article'); // LONGTEXT
$table->json('meta_data'); // JSON
$table->uuid('id')->primary(); // UUID
Числовые типы
// Целые числа
$table->integer('age'); // INT
$table->tinyInteger('status'); // TINYINT
$table->smallInteger('year'); // SMALLINT
$table->mediumInteger('score'); // MEDIUMINT
$table->bigInteger('views'); // BIGINT
// Числа с плавающей точкой
$table->float('price', 8, 2); // FLOAT(8,2)
$table->double('amount', 15, 8); // DOUBLE(15,8)
// Десятичные числа (точные)
$table->decimal('salary', 10, 2); // DECIMAL(10,2)
$table->boolean('is_active'); // BOOLEAN
Логические типы
$table->boolean('is_active'); // BOOLEAN
Дата и время
$table->date('birthday'); // DATE
$table->dateTime('created_at'); // DATETIME
$table->dateTimeTz('created_at'); // DATETIME with timezone
$table->time('start_time'); // TIME
$table->timeTz('start_time'); // TIME with timezone
$table->timestamp('created_at'); // TIMESTAMP
$table->timestampTz('created_at'); // TIMESTAMP with timezone
$table->year('founded'); // YEAR
// Автоматические метки
$table->timestamps(); // created_at и updated_at
$table->softDeletes(); // deleted_at для мягкого удаления
Бинарные типы
$table->binary('image_data'); // BLOB
Модификаторы столбцов
Модификаторы уточняют поведение колонки: можно ли хранить NULL, какое значение по умолчанию, нужен ли индекс и т.д. Модификаторы применяются цепочкой после метода типа.
$table->string('name')
->nullable() // Разрешить NULL
->default('John Doe') // Значение по умолчанию
->unique() // Уникальное значение
->index() // Индекс
->primary() // Первичный ключ
->comment('User full name'); // Комментарий
$table->integer('position')
->unsigned() // Беззнаковое целое
->autoIncrement() // Автоинкремент
->after('id'); // Разместить после колонки
$table->decimal('price', 10, 2)
->default(0.00)
->change(); // Изменить существующую колонку
Внешние ключи
Внешние ключи обеспечивают целостность данных на уровне базы данных. Они связывают таблицы и автоматически контролируют изменения связанных записей.
Создание внешних ключей
use Imhotep\Facades\Schema;
Schema::create('posts', function (Table $table) {
$table->id();
$table->foreignId('user_id')
->constrained('users') // Ссылка на users.id
->onDeleteCascade(); // Удаление постов при удалении пользователя
$table->foreignId('category_id')
->references('id')->on('categories')
->onDeleteRestrict(); // Запрет удаления категории с постами
$table->foreign(['user_id', 'role_id'])
->references(['id', 'role_id'])->on('user_roles')
->onUpdateCascade();
});
Действия ON DELETE и ON UPDATE
При создании внешнего ключа можно указать, что произойдет с зависимыми записями при удалении или обновлении родительской записи.
-
CASCADE- Автоматически удалить/обновить зависимые записи; -
RESTRICT- Запретить удаление/обновление, если есть зависимые записи; -
SET NULL- Установить NULL в поле внешнего ключа; -
SET DEFAULT- Установить значение по умолчанию в поле внешнего ключа; -
NO ACTION- Ничего не делать (проверка в конце транзакции).
Для каждого действия есть соответствующий метод:
| ON DELETE | ON UPDATE |
|---|---|
| onDeleteCascade() | onUpdateCascade() |
| onDeleteRestrict() | onUpdateRestrict() |
| onDeleteSetNull() | onUpdateSetNull() |
| onDeleteSetDefault() | onUpdateSetDefault() |
// ON DELETE CASCADE — удаление пользователя удаляет все его посты
$table->foreignId('user_id')
->constrained('users')
->onDeleteCascade();
// ON DELETE RESTRICT — нельзя удалить категорию, если есть посты
$table->foreignId('category_id')
->constrained('categories')
->onDeleteRestrict();
// ON DELETE SET NULL — при удалении пользователя,
// поле user_id становится NULL
$table->foreignId('user_id')
->constrained('users')
->onDeleteSetNull();
// ON DELETE SET DEFAULT — при удалении пользователя,
// поле user_id получает значение по умолчанию
$table->foreignId('user_id')
->constrained('users')
->onDeleteSetDefault();
// ON UPDATE CASCADE — при изменении ID пользователя,
// обновляются все связанные записи
$table->foreignId('user_id')
->constrained('users')
->onUpdateCascade();
// Комбинация ON DELETE и ON UPDATE
$table->foreignId('user_id')
->constrained('users')
->onDeleteCascade()
->onUpdateCascade();
Удаление внешних ключей
use Imhotep\Facades\Schema;
Schema::table('posts', function (Table $table) {
// Удаление по имени колонки
$table->dropForeign(['user_id']);
// Удаление по имени внешнего ключа
$table->dropForeign('posts_user_id_foreign');
});
Индексы
Индексы ускоряют поиск данных, но замедляют операции вставки и обновления. Используйте их осознанно:
- primary — для первичных ключей (autoincrement)
- unique — для уникальных значений (email, slug)
- index — для часто фильтруемых колонок
- fulltext — для полнотекстового поиска
- spatial — для геоданных
Создание индексов
use Imhotep\Facades\Schema;
Schema::create('posts', function (Table $table) {
// Простые индексы
$table->index('title'); // INDEX
$table->index(['title', 'content']); // Составной индекс
// Уникальные индексы
$table->unique('slug'); // UNIQUE INDEX
$table->unique(['category_id', 'slug']); // Составной уникальный
// Первичный ключ
$table->primary('id'); // PRIMARY KEY
$table->primary(['user_id', 'role_id']); // Составной первичный ключ
// Полнотекстовый индекс
$table->fulltext('content'); // FULLTEXT INDEX
// Пространственный индекс (для гео-данных)
$table->spatial('location'); // SPATIAL INDEX
// Индекс с пользовательским именем
$table->index('title', 'idx_posts_title');
});
Добавление индексов в существующую таблицу
use Imhotep\Facades\Schema;
Schema::table('posts', function (Table $table) {
$table->index('title');
$table->unique('slug');
});
Удаление индексов
use Imhotep\Facades\Schema;
Schema::table('posts', function (Table $table) {
// По имени колонки
$table->dropIndex(['title']);
$table->dropUnique(['slug']);
$table->dropPrimary(['id']);
// По имени индекса
$table->dropIndex('idx_posts_title');
$table->dropUnique('posts_slug_unique');
});
Особенности СУБД
PostgreSQL
- Поддерживает
serialиbigserialтипы - Есть
timestampTzиtimeTzдля работы с часовыми поясами - Поддерживает
jsonbтип
MySQL
- Поддерживает
tinyInteger,mediumInteger - Есть
enumтип - Поддерживает
charsetиcollateдля колонок
SQLite
- Ограниченная поддержка
ALTER TABLE - Нельзя удалять или переименовывать колонки в одной транзакции