Migrations de base de données
Les migrations de base de données offrent des modifications du schéma contrôlées par version et réversibles. Elles garantissent des états de base de données cohérents dans les environnements de développement, d’intermédiaire et de production.
Structure de migration
Section intitulée « Structure de migration »Nommage des fichiers
Section intitulée « Nommage des fichiers »migrations/├── 001_create_articles_table.php├── 002_add_status_column.php├── 003_create_categories_table.php├── 004_add_indexes.php└── 005_add_foreign_keys.phpClasse de migration
Section intitulée « Classe de migration »<?phpdeclare(strict_types=1);
return new class { public function up(\XoopsDatabase $db): void { $table = $db->prefix('mymodule_articles');
$sql = "CREATE TABLE IF NOT EXISTS `{$table}` ( `id` VARCHAR(26) NOT NULL COMMENT 'ULID identifier', `title` VARCHAR(255) NOT NULL, `content` MEDIUMTEXT, `status` ENUM('draft', 'published', 'archived') DEFAULT 'draft', `author_id` INT UNSIGNED NOT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_status` (`status`), KEY `idx_author` (`author_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
$db->queryF($sql); }
public function down(\XoopsDatabase $db): void { $table = $db->prefix('mymodule_articles'); $db->queryF("DROP TABLE IF EXISTS `{$table}`"); }};Opérations courantes
Section intitulée « Opérations courantes »Ajouter des colonnes
Section intitulée « Ajouter des colonnes »public function up(\XoopsDatabase $db): void{ $table = $db->prefix('mymodule_articles');
// Ajouter une seule colonne $db->queryF("ALTER TABLE `{$table}` ADD COLUMN `views` INT UNSIGNED DEFAULT 0 AFTER `status`");
// Ajouter plusieurs colonnes $db->queryF("ALTER TABLE `{$table}` ADD COLUMN `summary` TEXT AFTER `content`, ADD COLUMN `featured` TINYINT(1) DEFAULT 0 AFTER `views`");}
public function down(\XoopsDatabase $db): void{ $table = $db->prefix('mymodule_articles'); $db->queryF("ALTER TABLE `{$table}` DROP COLUMN `views`, DROP COLUMN `summary`, DROP COLUMN `featured`");}Modifier des colonnes
Section intitulée « Modifier des colonnes »public function up(\XoopsDatabase $db): void{ $table = $db->prefix('mymodule_articles');
// Changer le type de colonne $db->queryF("ALTER TABLE `{$table}` MODIFY COLUMN `title` VARCHAR(500) NOT NULL");
// Renommer la colonne $db->queryF("ALTER TABLE `{$table}` CHANGE `summary` `excerpt` TEXT");}Ajouter des index
Section intitulée « Ajouter des index »public function up(\XoopsDatabase $db): void{ $table = $db->prefix('mymodule_articles');
// Index sur une seule colonne $db->queryF("CREATE INDEX `idx_created` ON `{$table}` (`created_at`)");
// Index composite $db->queryF("CREATE INDEX `idx_status_date` ON `{$table}` (`status`, `created_at`)");
// Index unique $db->queryF("CREATE UNIQUE INDEX `idx_slug` ON `{$table}` (`slug`)");
// Index de texte intégral $db->queryF("CREATE FULLTEXT INDEX `idx_search` ON `{$table}` (`title`, `content`)");}
public function down(\XoopsDatabase $db): void{ $table = $db->prefix('mymodule_articles'); $db->queryF("DROP INDEX `idx_created` ON `{$table}`"); $db->queryF("DROP INDEX `idx_status_date` ON `{$table}`"); $db->queryF("DROP INDEX `idx_slug` ON `{$table}`"); $db->queryF("DROP INDEX `idx_search` ON `{$table}`");}Clés étrangères
Section intitulée « Clés étrangères »public function up(\XoopsDatabase $db): void{ $articles = $db->prefix('mymodule_articles'); $categories = $db->prefix('mymodule_categories');
$db->queryF("ALTER TABLE `{$articles}` ADD CONSTRAINT `fk_article_category` FOREIGN KEY (`category_id`) REFERENCES `{$categories}` (`id`) ON DELETE SET NULL ON UPDATE CASCADE");}
public function down(\XoopsDatabase $db): void{ $articles = $db->prefix('mymodule_articles'); $db->queryF("ALTER TABLE `{$articles}` DROP FOREIGN KEY `fk_article_category`");}Exécuteur de migration
Section intitulée « Exécuteur de migration »Exécuter les migrations
Section intitulée « Exécuter les migrations »function xoops_module_update_mymodule(\XoopsModule $module, $previousVersion){ $db = \XoopsDatabaseFactory::getDatabaseConnection(); $migrator = new MigrationRunner($db, $module->dirname());
try { $migrator->migrate(); return true; } catch (\Exception $e) { $module->setErrors($e->getMessage()); return false; }}Classe d’exécuteur de migration
Section intitulée « Classe d’exécuteur de migration »class MigrationRunner{ private string $migrationsPath; private string $table;
public function __construct( private \XoopsDatabase $db, private string $moduleName ) { $this->migrationsPath = XOOPS_ROOT_PATH . "/modules/{$moduleName}/migrations"; $this->table = $db->prefix("{$moduleName}_migrations"); }
public function migrate(): void { $this->createMigrationsTable(); $executed = $this->getExecutedMigrations();
foreach ($this->getPendingMigrations($executed) as $file) { $this->runMigration($file); } }
private function runMigration(string $file): void { $migration = require $this->migrationsPath . '/' . $file; $migration->up($this->db);
$this->db->queryF( "INSERT INTO `{$this->table}` (migration, executed_at) VALUES (?, NOW())", [$file] ); }
public function rollback(int $steps = 1): void { $migrations = $this->getExecutedMigrations(); $toRollback = array_slice(array_reverse($migrations), 0, $steps);
foreach ($toRollback as $file) { $migration = require $this->migrationsPath . '/' . $file; $migration->down($this->db);
$this->db->queryF( "DELETE FROM `{$this->table}` WHERE migration = ?", [$file] ); } }}Meilleures pratiques
Section intitulée « Meilleures pratiques »- Un changement par migration - Garder les migrations ciblées
- Toujours écrire les méthodes down - Activer les restaurations
- Tester les deux directions - Vérifier up() et down()
- Utiliser les transactions - Envelopper les migrations complexes
- Ne pas modifier les vieilles migrations - Créer de nouvelles migrations à la place
- Sauvegarder avant d’exécuter - Particulièrement en production
Documentation connexe
Section intitulée « Documentation connexe »- Schéma-de-base-de-données - Conception du schéma
- Opérations-de-base-de-données - Exécution de requête
- ../xoops_version.php - Manifeste du module
- ../../07-XOOPS-4.0/Architecture-XOOPS-4.0 - Architecture moderne