İçeriğe geç

Veritabanı ve migration

Modül şeması iki parçanın uyumudur: SQL migration dosyaları şemayı kurar, Doctrine varlıkları (#[ORM\Entity]) o şemayı PHP nesnelerine eşler. İkisini birlikte, birbirine uygun yazarsınız.

Tablo adlandırma

Modül tabloları cp_<modul>_* önekini taşır: cp_notes, cp_docs_pages, cp_portal_polls. Önek, çekirdek ve diğer modül tablolarıyla çakışmayı önler ve tables() listesinde neyi sileceğinizi açık eder.

Migration dosyaları

Şemayı Resources/migrations/00N_ad.sql dosyalarıyla kurarsınız:

CREATE TABLE IF NOT EXISTS cp_ornek_items (
    id INT AUTO_INCREMENT NOT NULL,
    title VARCHAR(255) NOT NULL,
    status VARCHAR(16) NOT NULL DEFAULT 'published',
    created_at DATETIME NOT NULL,
    PRIMARY KEY (id),
    INDEX idx_ornek_status (status)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;

Her tablo utf8mb4 + utf8mb4_unicode_ci + InnoDB olmalıdır; emoji ve her dil güvenle saklanır.

Çalışma kuralları

Kural Neden
Dosyalar ada göre sırayla çalışır 001_, 002_ önekleri sırayı belirler
Uygulananlar dosya adıyla kaydedilir İçerik sağlaması (checksum) yoktur; ad "yapıldı" demektir
Uygulanmış dosya dondurulur Değiştirmek eski sitelere yansımaz; düzeltme için yeni dosya yazın
Her ifade idempotent olmalı Defter sıfırlanırsa ya da yedek geri yüklenirse dosya tekrar çalışabilir

Idempotent kalıplar

-- Tablo
CREATE TABLE IF NOT EXISTS cp_ornek_items ( ... );

-- Başlangıç verisi: yönetici değiştirdiyse ezme
INSERT IGNORE INTO cp_ornek_settings (name, value) VALUES ('mode', 'default');

-- Sütun ekleme: information_schema ile korunmuş (her ifade kendi satırlarında, sonunda ";" ve satır sonu)
SET @cp_ornek_summary := (
    SELECT COUNT(*) FROM information_schema.COLUMNS
    WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'cp_ornek_items' AND COLUMN_NAME = 'summary'
);
SET @cp_ornek_summary_sql := IF(
    @cp_ornek_summary = 0,
    'ALTER TABLE cp_ornek_items ADD COLUMN summary VARCHAR(500) DEFAULT NULL',
    'DO 0'
);
PREPARE cp_ornek_summary_stmt FROM @cp_ornek_summary_sql;
EXECUTE cp_ornek_summary_stmt;
DEALLOCATE PREPARE cp_ornek_summary_stmt;

ALTER TABLE ADD COLUMN doğrudan idempotent değildir (ikinci çalışmada "sütun zaten var" hatası verir); yukarıdaki koruma bu yüzden vardır. Aynı şekilde ADD INDEX ve DROP COLUMN için de korumalı yazın.

Dosya nasıl bölünür

Çalıştırıcı SQL dosyasını ; + satır sonu kuralıyla ifadelere böler ve her ifadeyi ayrı çalıştırır. Bu yüzden: her ifadeyi ; ve satır sonuyla bitirin; bir metin sabitinin içinde ; ardından satır sonu yazmayın; tam satır -- yorumları ayıklanır, /* ... */ blok yorumları da güvenlidir. Aynı satırda birden çok ifade yazmayın.

Varlıklar (Doctrine)

#[ORM\Entity]
#[ORM\Table(name: 'cp_ornek_items')]
class Item
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: Types::INTEGER)]
    private ?int $id = null;

    #[ORM\Column(type: Types::STRING, length: 255)]
    public string $title = '';

    #[ORM\Column(name: 'created_at', type: Types::DATETIME_IMMUTABLE)]
    public \DateTimeImmutable $createdAt;
}
  • Modülün Entity/ klasörü Doctrine'e otomatik tanıtılır.
  • SQL ile varlık birebir uyuşmalı: tür, uzunluk, NULL durumu, sütun adı. Uyuşmazlık, doktrin şema denetiminde fark olarak çıkar.
  • DATETIME_IMMUTABLE kullanın, DateTime değil.
  • Sütun adları SQL tarafında snake_case, PHP özelliklerinde camelCase olur: #[ORM\Column(name: 'created_at')].

İlişkiler ve yabancı anahtar

Modül içi ilişkiler için yabancı anahtar kullanabilirsiniz (ON DELETE CASCADE gibi). Çekirdek tablolarına (kullanıcı, node) yabancı anahtar bağlamayın; sütunu düz tamsayı olarak tutun ve uygulama katmanında kontrol edin. Böylece çekirdek şeması değişse ya da modül devre dışı kalsa bütünlük bozulmaz. tables() listesinde çocuk tabloları önce yazın (silme sırası).

Alan tanımı ve başlangıç verisi

Node türlerine alan eklemek de bir migration işidir: cp_field_definitions tablosuna INSERT IGNORE ile satır eklenir. IGNORE önemlidir, çünkü yönetici AACP → Alanlar'dan ayarı değiştirmiş olabilir ve yeniden uygulanan dosya onu ezmemelidir:

INSERT IGNORE INTO cp_field_definitions
  (bundle, name, type, label, help, required, cardinality, translatable, queryable,
   field_group, weight, settings, view_capability, edit_capability, created_at, updated_at)
VALUES
  ('ornek', 'summary', 'textarea', 'Özet', 'Listelerde görünür.', 0, 1, 0, 0, NULL, 0, '{}', NULL, NULL, NOW(), NOW());

Alan türleri: text, textarea, rich_text, boolean, integer, decimal, email, url, date, datetime, select, reference, image, file. Ayrıntı: Formlar ve Field API ve Alan sistemi.

Taksonomi sözlükleri için de aynı kalıp: INSERT IGNORE INTO cp_vocabularies (...).

Yükseltme: yeni sürüm, yeni dosya

  1. 002_ornek_summary.sql dosyasını ekleyin (eskisine dokunmayın).
  2. module.json içinde sürümü artırın.
  3. Yayınlayın. Sitede cp:update modülün sürüm farkını görür ve yalnızca yeni dosyayı uygular.

Veri düzeltmesi gerekiyorsa (sütunu geri doldurmak, değer yeniden yazmak) SQL yeterli değilse bir UpdateHookInterface yazın: bkz. Güncelleme sistemi.

Kaldırma

cp:module:deactivate Ornek veriyi silmez. --purge ile kaldırma kancası çalışır, tables() listesindeki tablolar (çocuklar önce) ve modülün ayarları silinir. Hangi veriyi sileceğinizi kullanıcıya açıkça söyleyin; bu geri alınamaz.

Tutarlılık denetimi

cp:doctor, uygulanmış bir migration'ın oluşturduğunu iddia ettiği tablo ve sütunların gerçekten var olup olmadığını denetler (module_sql_integrity). Kaymayı raporlar ama onarmaz; onarım, ilgili defter kaydını unutturan açık bir güncelleme kancasıyla yapılır (idempotent dosya yeniden denenir).

Geliştirme ipuçları

  • Yeni bir migration'ı denerken test veritabanında iki kez çalıştırın; ikinci çalışma hata vermemeli.
  • Doctrine şema farkını görmek için: php cp-core/bin/console doctrine:schema:validate.
  • Büyük tabloda ALTER kilitleyebilir; canlıda büyük tablo değişikliğini bakım modunda yapın.
  • Sorgu yazarken liste ekranlarında N+1'den kaçının; bkz. Önbellek ve performans.
Son güncelleme: 13.10.2026