Koleksiyon & Blok Oluşturma
Data Model'de bir alan/koleksiyon değişikliği yaptığında ve bunu bir Vue bileşeninde (blok) tipli olarak kullanmak istediğinde izlenecek adımlar.
1. Koleksiyon oluşturma
/admin/schema → "Yeni collection". Ad, görüntü adı, görünüm şablonu ve ilk alanları girip oluştur. Tekil koleksiyonlar (header/footer gibi) için "Tekil kayıt" kutusunu işaretle.
Var olan bir koleksiyona sonradan alan eklemek/silmek/düzenlemek için /admin/schema/[collection] sayfasını kullan.
Arka planda ne oluyor
UI isteği POST /api/collections veya /api/fields/* ile server/engine/ddl/service.ts'e gidiyor. Orada fiziksel tablo (Knex) + mcms_collections/mcms_fields metadata'sı tek transaction'da, anında yazılıyor — ayrıca bir "apply" adımı yok. Bu yalnızca dev ortamında açık (capabilities.schemaEditing); prod'da şema düzenleme route'ları tamamen devre dışı/tree-shake'lenmiş.
Bir alanı localized yapmak (veya geri almak) da aynı sayfadan, anında ve mevcut veriyi otomatik taşıyarak çalışır — alan ana tablo ile <collection>_locales companion tablosu arasında veri kaybı olmadan taşınır.
2. Tip güvenliği: "Pull types"
Şema değişikliğinden sonra /admin/schema → "Pull types"'a bas. Bu, drizzle-kit introspect'i canlı veritabanına karşı çalıştırıp server/db/schema-dynamic/schema.generated.ts dosyasını yeniden üretir.
Önemli — bu dosyanın rolü
- Sadece tip çıkarımı (
InferSelectModel) için var — component'lerde IntelliSense/tip güvenliği sağlar. - Runtime source of truth değil — içerik okuma/yazma hâlâ metadata-driven query motorundan (
server/engine/query/*) gidiyor. Bu dosyadan hiçbir yerdeimportile veri çekilmiyor/yazılmıyor. - Elle düzenlenmez — dosyanın başında bunu belirten bir uyarı var. Bir şema değişikliğinden sonra bayatlar; tekrar "Pull types" ile güncellenir.
3. Yeni bir blok (Page Builder) oluşturma
- Koleksiyonu oluştur — adı
block_ile başlasın (ör.block_testimonial_grid) ve "hidden" işaretli olsun (içerik listesinde tek başına görünmesin, sadece sayfa builder'dan eklensin). UI'dan ya daserver/collections.config.ts'e kod olarak eklenebilir — ikisi de aynı motoru kullanır. - Vue bileşenini yaz —
app/blocks/BlockX.vue, tek birdataprop'u alır. - Registry'e ekle —
app/blocks/registry.ts'tekiblockRegistrymap'ineblock_x: BlockXsatırını ekle. - "Pull types" çalıştır (yukarıdaki adım 2), yeni koleksiyonun tipi
schema.generated.ts'e düşsün. - Prop tipini yaz —
app/blocks/BlockHero.vue'daki örneği takip et:
import type { InferSelectModel } from "drizzle-orm";
import type { block_x } from "~~/server/db/schema-dynamic/schema.generated";
defineProps<{ data: InferSelectModel<typeof block_x> | null }>();4. Localized alanlar varsa
Localized alanlar (ör. çeviri gerektiren title/headline) ana tabloda değil, block_x_locales companion tablosunda yaşar — ama query motoru şu anki dil için değerleri satırın üzerine otomatik merge eder (server/engine/query/relations.ts'teki mergeLocales). Bu yüzden tip de iki tabloyu birleştirmeli:
import type { InferSelectModel } from "drizzle-orm";
import type { block_x, block_x_locales } from "~~/server/db/schema-dynamic/schema.generated";
type BlockXData = InferSelectModel<typeof block_x>
& Omit<InferSelectModel<typeof block_x_locales>, "id" | "parent_id" | "locale">;
defineProps<{ data: BlockXData | null }>();Omit tercih edilir, Pick<..., "title" | "headline"> gibi alan adlarını elle yazmak yerine — yeni bir localized alan eklendiğinde bu satırı değiştirmeden otomatik tipe dahil olur.
5. İlişkisel alanlar (m2o / o2m / m2m / m2a)
Bir buton grubu, galeri, SSS listesi gibi ilişkili alanlar public render'da (expandM2O ile) otomatik olarak dolu obje halinde gelir — editör tarafında ise ham id kalır (seçim input'u için). Bu alanların fiziksel şekli schema.generated.ts'e yansımaz (junction/ilişki tabloları introspect'te ayrı tablolar olarak görünür); component'te genelde tam tip yerine unknown ya da ilgili alt component'in kendi prop tipiyle çalışmak yeterli — bkz. app/blocks/BlockButtons.vue.
6. Mimari & Split-Runtime Modeli
Proje tek kod tabanı (Monolith Nuxt) ile iki farklı ortama derlenir:
Dev / Local (Node.js Target)
- Tam Authoring & DDL Motoru (Knex DDL + Drizzle DML)
- Canlı Şema ve Koleksiyon Düzenleme (
capabilities.schemaEditing = true) - Server-side Sharp ile Görsel İşleme
- Redis / Memory Cache ve Local FS Storage
Prod (Cloudflare Workers Edge Target)
- İçerik Editörlüğü & Edge Serve (Drizzle DML + Hyperdrive)
- Şema Düzenleme Kilitli & Tree-shake Edilmiş (
capabilities.schemaEditing = false) - Client-side Resize / CF Images + R2 Storage
- Cloudflare KV Cache + Request-scoped Hyperdrive DB Connection
Not: DDL mutasyonları sadece Dev ortamında gerçekleşir. Şema snapshot'ı Git'e commit edilir ve CI/CD pipeline'ı üzerinden Prod Neon veritabanına drizzle-kit migrate ile yansıtılır.
7. Draft / Publish & Live Preview (Canlı Önizleme)
İçerik yaşam döngüsü Draft (Taslak) ve Published (Yayınlanmış) durumlarıyla yönetilir:
- Draft Veri: İçerik güncellendiğinde taslak delta
mcms_revisionstablosunda saklanır. Public siteye yansımaz. - Live Preview: Özel
preview_tokenile çağrılan istekler public cache'i tamamen bypass eder ve ham taslak veriyi render eder. - Çift Sekme / Split View Senkronizasyonu: Editör panelinde yapılan değişiklikler
BroadcastChannelAPI üzerinden önizleme penceresine anında iletilir ve sayfa/blok yeniden çekilir. - Publish (Yayınlama): Taslak revizyonu canlı tabloya yazılır ve versiyon token'ı güncellenerek cache invalidated edilir.
8. Cache & Versiyon-Namespace Stratejisi
Anonim ziyaretçiler için yüksek performanslı cache, yetkili editörler için ise sıfır-gecikmeli tazelik hedeflenmiştir:
- Versiyon Token: Cache anahtarları koleksiyon bazlı versiyon token'larını içerir (
articles:locale=tr:vToken). İçerik yayımlandığında token güncellenir ve eski cache bayatlar. - Admin Bypass: Oturum açmış içerik editörlerinin istekleri public cache'i otomatik bypass eder, böylece yapılan düzenlemeler anında gözlemlenir.
- Hyperdrive Cache Ayarı: Hyperdrive'ın kendi dahili cache'i disabled konumdadır; read-your-writes tutarlılığı Nuxt seviyesindeki KV/Redis katmanıyla sağlanır.
9. Best Practices, Kod Kalitesi & Test Rehberi
Geliştirme Standartları & İpuçları:
- AST -> SQL Güvenliği: Filtre sorgularında Drizzle
sqlşablonu kullanılarak SQL Injection riskleri tamamen engellenmiştir. Ham SQL birleştirmelerinden kaçının. - Request-scoped DB/Auth: Server handlers ve auth işlemlerinde modül seviyesinde tekil DB/Auth istemcileri tutmayın; request-scoped
getDb()verequireUser()kullanın. - Tip Çıkarımı: Vue bileşenlerinde manuel tip yazmak yerine
InferSelectModelveOmittüretimlerini kullanın. - Derleme Doğrulaması: Değişiklik sonrası
npx tsc --noEmitvenpm run build:cfkomutlarıyla hem Node hem Cloudflare build hedeflerini test edin.