Міграції та база даних

Міграції та база даних

Міграції описують структуру таблиць у коді. Вони ідемпотентні: вже застосовані пропускаються (облік у таблиці 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 '%...'.