Veritabanı Şemasını Değiştirme
Tablolarınızı, platformun size verdiği bir anda kendi kodunuz kurar ve taşır; aynı kodun tekrar çalıştırılmaya da güvenli olması gerekir.
Genel Bakış
Bir migration dizini ya da artırılacak bir sürüm dosyası yoktur. Depolamaya ihtiyaç duyan bir modül onu kendi yaşam döngüsünde, etkinleştirildiğinde kurar ve eski bir kurulumu da aynı yerde ileri taşır. Yani tek bir metot hem kurma hem yükseltme yolunu tutar ve içindeki her ifadenin, ikinci çalıştırmayı etkisiz kılan kendi koşulu olmalıdır.
Çekirdeğin kendi tabloları sizin değiştireceğiniz tablolar değildir. Ürünün gönderdiği bir tabloya kolon eklemek, bir güncellemenin geri alacağı ve daha kötüsü çakışabileceği bir değişikliktir. İhtiyacınız olanı kendi tablonuzda saklayın ve join edin.
Ön Koşullar
- Bir modül; çünkü tablo sahipliği burada yaşar. Depolamaya ihtiyaç duyan bir kanca dinleyicisi de aynı sebeple bir modüle aittir.
- Ürününkiyle çakışamayacak bir tablo adı. Başına modülünüzün adını koyun.
Adım Adım
Modül Etkinleştirilince Kur
- Modülünüzün yaşam döngüsündeki etkinleştirme adımını tanımlayın ve şema kontrolünüzü oradan çağırın.
- Kontrolde, kurmadan önce tablonun var olup olmadığını sorun. Zaten kurulu bir modülü etkinleştirmek hata vermemelidir.
- Modülü panelden etkinleştirin ve tablonun göründüğünü doğrulayın.
Aynı Metotta Taşı
- Tablo zaten varsa, eski bir biçimi ileri taşımak için aynı metodu kullanın: eski kolon hâlâ orada mı diye sorun ve yalnızca oradaysa değiştirin.
- Her adımı saklanan bir sürüm numarasıyla değil kendi koşuluyla koruyun; böylece bir sürümü atlamış kurulum da aynı noktaya yakınsar.
- Kontrolü arka arkaya iki kez çalıştırın ve ikincisinin hiçbir şey yapmadığını doğrulayın.
Yalnızca Boş Tabloyu Tohumla
- Özelliğinizin başlangıç satırlarına ihtiyacı varsa, onları yalnızca tablo boşken ekleyin.
- Her etkinleştirmede tohumlama yapmayın; modülü kapatıp yeniden açan bir operatör ya kopya satırlar alır ya da kendi düzenlemelerini sessizce kaybeder.
- Kapatıp yeniden açın, sonra satır sayısının değişmediğini doğrulayın.
Referans
Platformun Çağırdıkları
'enable' ya da 'disable' alır, metot çiftinizi çalıştırır ve yeni durumu ancak ondan sonra modülün yapılandırmasına yazar.
// Modülünüz tanımlar, operatör anahtarı çevirdiğinde platform çağırır. "enable"da varsa
// önce activate(), sonra enable() çalışır; "disable"da deactivate()/disable() çifti aynısını
// yapar. Yeni durum YALNIZCA sonuncusu doğru bir değer döndürdüyse yazılır; yani yanlış bir
// dönüş ya da fırlatılan bir exception, modülü yarım kurulu bırakmak yerine tıklamayı reddeder.
public function enable(): bool;
public function disable(): bool;
// Kendi adımlarınız, enable() içinden çağrılır. Hem kurma hem yükseltme yolu burada yaşar;
// yani ikisi de her etkinleştirmede, her yeniden etkinleştirmede ve her güncellemeden sonra
// çalışır.
private function check_database(): void; // tablolar
private function check_columns(): void; // sonraki bir sürümün eklediği kolonlar
private function seed(): void; // başlangıç satırları, bir kez
Sorgu Katmanının Şemaya Bakan Kısmı
public static function hasTable($table = ''); // bool, SHOW TABLES LIKE ile - önek eklenmez
public static function exec($arg = ''); // int etkilenen satır; başarısızlıkta 0, FIRLATMADAN
public static function query($statement, $isthis = false); // PDOStatement ya da başarısızlıkta false - o da fırlatmadan
public static function getAssoc($statement = false); // o statement'ın tek satırı; satır yoksa false
public static function getErrorMessage(); // exec() ya da query() neden boş döndü
public static function getPrefix(): string; // kurulum bir önek tanımlamışsa şema öneki
// DDL exec()/query() üzerinden çalışır; çünkü zincirli kurucu yalnızca DML yazar. Bu ikisinden
// hiçbiri exception fırlatmaz: düşen bir CREATE ya da ALTER 0 / false döner ve hiçbir şey demez.
İfade Başına Bir Koşul
| Eklediğiniz şey | İkinci çalıştırmayı etkisiz kılan koşul | İfade |
|---|---|---|
| bir tablo | WDB::hasTable($t) false |
CREATE TABLE |
| bir kolon | SHOW COLUMNS FROM $t LIKE 'col' satır döndürmüyor |
ALTER TABLE ... ADD COLUMN |
| adı değişen kolon | eski ad hâlâ satır döndürüyor | ALTER TABLE ... CHANGE |
| kaldırılan kolon | ad hâlâ satır döndürüyor | ALTER TABLE ... DROP COLUMN |
| başlangıç satırları | tablonun satır sayısı 0 | INSERT |
Örnek
public function enable(): bool
{
$this->check_database();
$this->check_columns();
return true;
}
private function check_database(): void
{
if (\WDB::hasTable(self::TABLE)) {
// Zaten kurulu: burası kurma değil yükseltme yolu. Her adımın kendi koşulu var;
// böylece bir sürümü atlamış kurulum da aynı noktaya yakınsar.
$old = \WDB::query("SHOW COLUMNS FROM `" . self::TABLE . "` LIKE 'ticket_id'");
if ($old && \WDB::getAssoc($old))
\WDB::exec("ALTER TABLE `" . self::TABLE . "` CHANGE `ticket_id` `owner_id` INT UNSIGNED NOT NULL DEFAULT 0");
return;
}
$created = \WDB::exec('CREATE TABLE `' . self::TABLE . '` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`owner_id` INT UNSIGNED NOT NULL DEFAULT 0,
`status` VARCHAR(32) NOT NULL DEFAULT "",
`ctime` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `owner_id` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
// exec() hatayı yutar; başarıyı varsaymak yerine sorun.
if (!\WDB::hasTable(self::TABLE))
throw new \Exception('Tablo kurulamadı: ' . \WDB::getErrorMessage());
$this->seed();
}
private function check_columns(): void
{
// Sonraki bir sürümün getirdiği kolonlar; tablo => kolon => tanım.
$columns = [
self::TABLE => ['provider' => 'VARCHAR(30) NOT NULL DEFAULT ""'],
];
foreach ($columns as $table => $definitions) {
if (!\WDB::hasTable($table)) continue;
foreach ($definitions as $column => $definition) {
$stmt = \WDB::query("SHOW COLUMNS FROM `` LIKE ''");
if ($stmt && \WDB::getAssoc($stmt)) continue;
\WDB::exec("ALTER TABLE `` ADD COLUMN `` ");
}
}
}
private function seed(): void
{
// Başlangıç satırları BİR KEZ girer. Modülü kapatıp yeniden açan bir operatör ne kopya
// satır almalı ne de kendi düzenlemelerini kaybetmeli.
$stmt = \WDB::select('COUNT(id) AS total')->from(self::TABLE);
$total = $stmt->build() ? (int) (($stmt->getAssoc() ?: [])['total'] ?? 0) : 0;
if ($total > 0) return;
\WDB::insert(self::TABLE, ['owner_id' => 0, 'status' => 'ready', 'ctime' => \DateManager::Now()]);
}
// Tek kullanımlık bir script: biçimi oku, adımı iki kez çalıştır, tekrar oku. "Tekrar
// çalıştırmaya güvenlidir" iddiası ancak iki okuma birbirini tuttuğunda bir şey ifade eder.
$module = Modules::getInstance('Addons', 'AcmeScanner');
$shape = static function (): array {
$rows = WDB::query('SHOW COLUMNS FROM `Acme_scans`');
$cols = $rows ? WDB::fetch_assoc($rows) : [];
$count = WDB::select('COUNT(id) AS total')->from('Acme_scans');
return [
'columns' => array_column($cols, 'Field'),
'rows' => $count->build() ? (int) (($count->getAssoc() ?: [])['total'] ?? 0) : 0,
];
};
$module->enable();
$first = $shape();
$module->enable();
$second = $shape();
echo $first === $second ? "idempotent\n" : "SAPMA VAR\n";
Tuzaklar
O tabloların sahibi bir güncellemedir. Kolonunuz hayatta kalabilir, düşürülebilir ya da ürünün aynı adla eklediği bir kolonla çakışabilir. Verinizi kendi tablonuzda tutun ve onlarınkine kimlik üzerinden join edin.
exec() ve query() bir SQL hatasında exception fırlatmaz: 0 ve false döner, sebebi de biri sorana kadar getErrorMessage() içinde bekler. Yalnız bu ikisini çağıran bir şema adımı, hiçbir şeyin kurulmadığı bir kurulumda da başarı bildirir. Biçimi geri okuyun ya da kontrol onu bulamadığında exception fırlatın.
Etkinleştirme, yeniden etkinleştirme ve güncelleme hepsi buraya uğrar. Her ifadenin ikinci çalıştırmayı etkisiz kılan bir koşulu olmalı; emin olmanın yolu da üzerine düşünmek değil, iki kez çalıştırıp biçimi karşılaştırmaktır.
Kapatıldığında tablonuzu düşürmek, yanlışlıkla yapılmış bir tıklamayı veri kaybına çevirir. Satırları bırakın; yeniden etkinleştirme verisini bıraktığı yerde bulur, gerçekten silinmesini isteyen operatör de bunu söyleyebilir.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.