Миграции и база данных
Миграции и база данных
Миграции описывают структуру таблиц в коде. Они идемпотентны: уже применённые пропускаются (учёт в таблице cl_celena_migrations), новые применяются автоматически — при включении плагина или на старте (для ядра).
Файл миграции
Лежит в plugins/<slug>/migrations/ (или core/Database/migrations/). Имя — по алфавиту, с числовым префиксом: 0001_create_reviews.php. Файл возвращает замыкание:
<?php
declare(strict_types=1);
use Celena\Core\Database\Blueprint;
use Celena\Core\Database\Connection;
use Celena\Core\Database\Schema;
return function (Schema $schema, Connection $conn): void {
$schema->create('reviews', function (Blueprint $t) {
$t->id(); // PK auto-increment
$t->string('author', 191)->default('');
$t->text('text')->nullable();
$t->integer('rating')->default(5);
$t->boolean('is_published')->default(false);
$t->json('meta')->nullable();
$t->datetime('created_at')->nullable();
$t->index('is_published');
});
};
Доступные типы Blueprint: id, string, text, integer, bigInteger, boolean, decimal, json, datetime, timestamps, плюс default(), nullable(), unique(), index().
Кросс-СУБД
Локальная разработка — на PostgreSQL, прод — на MySQL. Поэтому:
- Используйте только
Blueprint-API иQueryBuilder— они компилируются под нужный драйвер. - Не пишите MySQL-only SQL (или PG-only). Например, сортировку версий делайте в PHP (
version_compare), а не SQL-функциями конкретной СУБД. - MySQL неявно коммитит DDL: после
CREATE TABLEтранзакция уже закрыта. Не смешивайте DDL и DML в одной миграции, если рассчитываете на откат.MigrationRunnerэто учитывает.
Префикс таблиц (cl_) добавляется автоматически: в коде обращайтесь к таблице без префикса (builder('reviews') → cl_reviews).
Запросы: QueryBuilder
// чтение
$rows = $conn->builder('reviews')
->select('id', 'author', 'rating')
->where('is_published', '=', true)
->whereIn('rating', [4, 5])
->orderBy('created_at', 'desc')
->limit(10)->offset(0)
->get();
$one = $conn->builder('reviews')->where('id', '=', 1)->first(); // ?array
$count = $conn->builder('reviews')->count();
// запись
$id = $conn->builder('reviews')->insert(['author' => 'Анна', 'rating' => 5]);
$conn->builder('reviews')->where('id', '=', 1)->update(['is_published' => true]);
$conn->builder('reviews')->where('id', '=', 1)->delete();
Сырой SQL — только через подготовленные выражения:
$stmt = $conn->run('SELECT count(*) FROM ' . $conn->table('reviews') . ' WHERE rating >= ?', [4]);
Никогда не конкатенируйте пользовательские данные в SQL — только параметры. См. Безопасность.
Транзакции
$conn->transaction(function () use ($conn) {
$conn->builder('orders')->insert([...]);
$conn->builder('order_items')->insert([...]);
});
transaction() корректно обрабатывает неявный commit DDL в MySQL.
Запуск
- Плагин: миграции прогоняются при включении и идемпотентно — на старте (если плагин активен, как в примере
plugin.php). - Ядро: на старте из
core/Database/migrations/. - CLI:
php bin/celenaсодержит команды для миграций и обслуживания.
На проде передDELETE/ALTERделайте бэкап и используйте узкие условияWHERE— без широкихLIKE '%...'.