Migracje w PHP - Wersjonowanie Struktury Bazy Danych
Czym są migracje bazy danych?
Migracje to sposób na kontrolę wersji struktury bazy danych. Podobnie jak Git śledzi zmiany w kodzie, migracje śledzą zmiany w schemacie bazy - tworzenie tabel, dodawanie kolumn, modyfikację indeksów. Każda migracja to odwracalna operacja, którą możesz zastosować lub cofnąć.
Dlaczego warto używać migracji?
- Kontrola wersji - każda zmiana schematu jest śledzona w kodzie
- Przenośność - łatwe przenoszenie zmian między środowiskami (dev, staging, production)
- Odwracalność - możliwość cofnięcia zmian za pomocą rollback
- Współpraca - wielu developerów może synchronizować strukturę bazy
- Atomowość - migracje wykonywane w transakcjach
Struktura pliku migracji
Każda migracja to klasa anonimowa z dwoma metodami: up()
(zastosuj zmiany) i down()
(cofnij zmiany):
<?php
use DJWeb\Framework\DBAL\Migrations\Migration;
use DJWeb\Framework\DBAL\Schema\Columns\*;
return new class extends Migration {
public function up(): void
{
$this->schema->createTable('products', [
new IntColumn('id', nullable: false, autoIncrement: true),
new VarcharColumn('name', length: 255),
new TextColumn('description'),
new IntColumn('price', unsigned: true),
new IntColumn('stock', unsigned: true, defaultValue: 0),
new EnumColumn('status', ['draft', 'published', 'archived']),
new DateTimeColumn('created_at', current: true),
new PrimaryColumn('id'),
]);
$this->schema->createIndex('products', 'idx_status', 'status');
}
public function down(): void
{
$this->schema->dropTable('products');
}
};
Tworzenie migracji - konwencje nazewnicze
Migracje używają timestamp jako prefixu nazwy pliku, co gwarantuje kolejność wykonania:
<?php
// Komenda CLI tworząca nową migrację
php djweb make:migration create_users_table
// Wygenerowany plik:
// database/migrations/20250130_143022_create_users_table.php
Zaawansowane operacje migracyjne
Modyfikacja istniejącej tabeli
<?php
return new class extends Migration {
public function up(): void
{
// Dodanie nowej kolumny
$this->schema->addColumn(
'users',
new VarcharColumn('phone', length: 20, nullable: true)
);
// Dodanie indeksu
$this->schema->createIndex('users', 'idx_phone', 'phone');
}
public function down(): void
{
$this->schema->dropIndex('users', 'idx_phone');
$this->schema->dropColumn('users', 'phone');
}
};
Migracja z modyfikacją danych
<?php
return new class extends Migration {
public function up(): void
{
// Dodaj kolumnę
$this->schema->addColumn(
'users',
new EnumColumn('role', ['user', 'admin'], defaultValue: 'user')
);
// Aktualizuj istniejące dane
$this->connection->execute(
"UPDATE users SET role = 'admin' WHERE email LIKE '%@admin.com'"
);
}
public function down(): void
{
$this->schema->dropColumn('users', 'role');
}
};
Komendy CLI do zarządzania migracjami
# Utworzenie nowej migracji
php djweb make:migration create_orders_table
# Wykonanie wszystkich pending migracji
php djweb migrate
# Cofnięcie ostatniej migracji
php djweb migrate:rollback
# Cofnięcie wszystkich migracji
php djweb migrate:reset
# Status migracji (które zostały wykonane)
php djweb migrate:status
DatabaseMigrationRepository - tracking wykonanych migracji
System śledzi wykonane migracje w specjalnej tabeli migrations
:
<?php
class DatabaseMigrationRepository
{
public function log(string $migration, int $batch): void
{
$this->connection->execute(
'INSERT INTO migrations (migration, batch) VALUES (?, ?)',
[$migration, $batch]
);
}
public function getRan(): array
{
return $this->connection->query(
'SELECT migration FROM migrations ORDER BY batch, migration'
);
}
public function getLastBatch(): array
{
$lastBatch = $this->connection->query(
'SELECT MAX(batch) as batch FROM migrations'
)[0]['batch'] ?? 0;
return $this->connection->query(
'SELECT migration FROM migrations WHERE batch = ?',
[$lastBatch]
);
}
}
MigrationResolver - ładowanie plików migracji
Resolver skanuje katalog database/migrations
i ładuje klasy migracji:
<?php
class MigrationResolver
{
public function resolve(string $path): array
{
$files = glob($path . '/*.php');
$migrations = [];
foreach ($files as $file) {
$migration = require $file;
$migrations[basename($file)] = $migration;
}
ksort($migrations); // sortowanie po timestamp
return $migrations;
}
}
Transakcje w migracjach
Każda migracja wykonywana jest w transakcji - jeśli wystąpi błąd, wszystkie zmiany są wycofywane:
<?php
try {
$this->connection->beginTransaction();
// Wykonanie migracji
$migration->up();
// Log do bazy
$repository->log($migrationName, $batch);
$this->connection->commit();
} catch (\Exception $e) {
$this->connection->rollback();
throw $e;
}
Praktyczne zastosowanie
W prawdziwym projekcie struktura migracji wygląda następująco:
database/
└── migrations/
├── 20250115_100000_create_users_table.php
├── 20250115_110000_create_posts_table.php
├── 20250120_143000_add_role_to_users.php
├── 20250125_092000_create_comments_table.php
└── 20250130_154500_add_indexes_to_posts.php
🎓 Zbuduj system migracji od podstaw!
W kursie PHP 8.4 implementujesz kompletny system migracji - od Migration base class przez DatabaseMigrationRepository, MigrationResolver, aż po komendy CLI (migrate, rollback, reset). Pełna obsługa transakcji i rollback.