تخطَّ إلى المحتوى

ترحيلات قاعدة البيانات

توفر ترحيلات قاعدة البيانات تغييرات قابلة للإرجاع والمراقبة بالإصدار على المخطط. تضمن حالات قاعدة البيانات المتسقة عبر بيئات التطوير والتدرج والإنتاج.

migrations/
├── 001_create_articles_table.php
├── 002_add_status_column.php
├── 003_create_categories_table.php
├── 004_add_indexes.php
└── 005_add_foreign_keys.php
migrations/001_create_articles_table.php
<?php
declare(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',
`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}`");
}
};
public function up(\XoopsDatabase $db): void
{
$table = $db->prefix('mymodule_articles');
// أضف عمود واحد
$db->queryF("ALTER TABLE `{$table}` ADD COLUMN `views` INT UNSIGNED DEFAULT 0 AFTER `status`");
// أضف أعمدة متعددة
$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`");
}
public function up(\XoopsDatabase $db): void
{
$table = $db->prefix('mymodule_articles');
// تغيير نوع العمود
$db->queryF("ALTER TABLE `{$table}` MODIFY COLUMN `title` VARCHAR(500) NOT NULL");
// إعادة تسمية العمود
$db->queryF("ALTER TABLE `{$table}` CHANGE `summary` `excerpt` TEXT");
}
public function up(\XoopsDatabase $db): void
{
$table = $db->prefix('mymodule_articles');
// فهرس عمود واحد
$db->queryF("CREATE INDEX `idx_created` ON `{$table}` (`created_at`)");
// فهرس مركب
$db->queryF("CREATE INDEX `idx_status_date` ON `{$table}` (`status`, `created_at`)");
// فهرس فريد
$db->queryF("CREATE UNIQUE INDEX `idx_slug` ON `{$table}` (`slug`)");
// فهرس النص الكامل
$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}`");
}
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`");
}
include/onupdate.php
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;
}
}
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]
);
}
}
}
  1. تغيير واحد لكل ترحيل - حافظ على الترحيلات مركزة
  2. اكتب طرق Down دائماً - تمكين الاسترجاع
  3. اختبر كلا الاتجاهين - تحقق من up() و down()
  4. استخدم المعاملات - غلف الترحيلات المعقدة
  5. لا تعديل الترحيلات القديمة - أنشئ جديد بدلاً من ذلك
  6. دعم احتياطي قبل التشغيل - خاصة في الإنتاج
  • Database-Schema - تصميم المخطط
  • Database-Operations - تنفيذ الاستعلام
  • ../xoops_version.php - بيان الوحدة
  • ../../07-XOOPS-4.0/XOOPS-4.0-Architecture - العمارة الحديثة