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.
Ç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,
NULLdurumu, sütun adı. Uyuşmazlık, doktrin şema denetiminde fark olarak çıkar. DATETIME_IMMUTABLEkullanın,DateTimedeğil.- Sütun adları SQL tarafında
snake_case, PHP özelliklerindecamelCaseolur:#[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
002_ornek_summary.sqldosyasını ekleyin (eskisine dokunmayın).module.jsoniçinde sürümü artırın.- Yayınlayın. Sitede
cp:updatemodü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
ALTERkilitleyebilir; 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.