İçeriğe geç

Alan sistemi (Field API)

Field API, içerik türlerine kod yazmadan özel alan eklemenin yoludur. Bir haber türüne "kaynak", bir etkinliğe "başlangıç tarihi", bir ürüne "fiyat" eklemek için şema değiştirmezsiniz; bir alan tanımı oluşturursunuz. Form, doğrulama, depolama, görüntüleme ve arama indeksi bu tanımdan türer.

Bu sistem Node (içerik) türleri içindir. Gerçek SQL sütunlu iş kayıtları (Resource) alanlarını Doctrine ile tanımlar; bkz. İçerik modeli.

Kavramlar

Kavram Açıklama
Bundle Alanların bağlı olduğu içerik türü (post, page, portal_news)
Alan tanımı (FieldDefinition) Bundle + ad + tür + etiket + ayarlar
Alan türü (FieldTypeInterface) Davranış: normalleştirme, doğrulama, indeks, ayar şeması
Widget O alanın formda hangi Symfony form türüyle düzenleneceği
Formatter / görüntüleme Alanın sayfada nasıl çizileceği
Görüntüleme modu (view mode) default, teaser, card: aynı içeriğin farklı bağlamlarda sunumu

Alan tanımı özellikleri

Özellik Anlamı
name Makine adı: küçük harf, rakam, alt çizgi; harfle başlar, en çok 63 karakter
type Alan türü kimliği (aşağıda)
label, help Etiket ve yardım metni
required Zorunlu mu
cardinality Değer sayısı: 1 tek, -1 sınırsız, ya da sabit sayı
translatable Çeviriye göre farklı değer taşıyabilir mi
queryable Düz alan indeksine yazılsın mı (filtre/sıralama için)
fieldGroup Formda ve görüntülemede gruplama
weight Sıra
settings Türe özgü ayarlar (ör. max_length, choices)

Alanlar AACP → Yapı → Alanlar ekranından yönetilir. Yetki: system.fields.manage.

Yerleşik alan türleri

Tür Ne saklar Ayar / not
text Tek satır metin max_length
textarea Çok satır düz metin
rich_text Zengin metin (metin biçimiyle) default_format (varsayılan basic_html)
integer Tam sayı Düz indekste int olarak aranabilir
decimal Ondalık sayı Düz indekste decimal
boolean Evet/hayır
date, datetime Tarih, tarih-saat Düz indekste datetime
email, url Doğrulanmış e-posta / URL
select Sabit listeden değer Seçenekler değer|etiket satırları
reference Başka bir varlığın kimliği (kullanıcı, belirli türde node, resource) target izinli bir dizedir, ham sınıf adı asla verilmez
image Medya kütüphanesinden bir image/* varlığın kimliği
file Medya kütüphanesinden bir varlığın kimliği

Türün gerçek kimliklerini görmek için: php cp-core/bin/console cp:debug.

Değer yaşam döngüsü

form girdisi ──▶ FieldValueNormalizer ──▶ FieldValidator ──▶ depolama (Node::data JSON)
                 (tür.normalize)          (tür.validate)         └─▶ düz indeks (queryable ise)
  1. Normalleştirme (FieldValueNormalizer): tek karar noktasıdır. Yalnızca bir alan tanımının desteklediği anahtarlar üretilir; ham istek dizileri olduğu gibi birleştirilmez (mass-assignment koruması).
  2. Doğrulama (FieldValidator): normalleştirilmiş, temiz değer üzerinde çalışır. Çok değerli alanlarda sayıyı denetler.
  3. Depolama: değerler Node::data JSON sütununa yazılır.
  4. İndeks: queryable alanlar node_field_index tablosuna tipli değerle yazılır.

Formlar

Bir modülün kendi formuna bundle alanlarını eklemek tek satırdır:

$this->fieldForms->add($builder, 'portal_news', $locale);

FieldableFormBuilder::add bundle'ın alanlarını fields adlı bir alt form olarak ekler; bundle'ın hiç alanı yoksa hiçbir şey yapmaz. Tür → form türü eşlemesini FieldWidgetResolver yapar; çok değerli alanları FieldsFormType bir CollectionType ile sarar.

Görsel ve dosya alanları

image / file alanları bir varlık kimliği saklar. Formda field-media betiği önizleme, "Kütüphaneden seç" ve "Kaldır" düğmelerini çizer. Şablonunuzun importmap listesine sırayla ekleyin:

{{ importmap(['app', 'media-picker', 'field-media']) }}

media-picker önce, field-media sonra gelmelidir. Medya modülü yoksa alan düz bir kimlik kutusu olarak kalır.

Çok değerli alanlar

cardinality değeri -1 ya da 1'den büyük olan alanlar bir koleksiyon olur; aynı betik + Ekle ve × düğmelerini ekler.

Şablonda alanları tek tek çizmek

{% for child in form.fields|filter(c => c.vars.name not in ['image']) %}
    {{ form_row(child) }}
{% endfor %}

Görüntüleme

Ön yüz şablonlarında Twig işlevleri kullanılır:

İşlev Ne yapar
cp_field(node, 'summary') Tek alanı çizer (HTML)
cp_field_value(node, 'summary') Ham değeri verir
cp_fields(node, 'grup') Bir alan grubunun tamamını çizer
cp_has_field(node, 'summary') Alan dolu mu
cp_fields_preload(nodes) Bir listedeki varlık referanslarını toplu yükler
cp_entity_view(node, 'teaser') Varlığı bir görüntüleme modunda çizer
{% do cp_fields_preload(items) %}
{% for item in items %}
    <article>
        <h2>{{ item.title }}</h2>
        {% if cp_has_field(item, 'summary') %}{{ cp_field(item, 'summary') }}{% endif %}
    </article>
{% endfor %}

FieldRenderer her alanın view_capability ayarına uyar (bir alanı yalnızca belli yetenekteki kullanıcılara gösterebilirsiniz) ve ek içerik sorgusu tetiklemez: değerler varlığın alan torbasında durur, referanslar toplu yüklenir. Liste şablonlarında N+1'den kaçınmak için cp_fields_preload kullanın.

Görüntüleme modları cp-core/config/view_modes.yaml içinde tanımlıdır (default, teaser, card). Hangi alanın hangi modda hangi formatter ile çizileceği AACP → Yapı → Görüntüleme ekranından ayarlanır.

Modül kurulumunda alan oluşturma

Bir modül, kendi türleri için alanları kurulumda tanımlar. FieldDefinitionSeeder::ensure() yalnızca eksik alanı oluşturur; bir editörün AACP'den ince ayar yaptığı alanı asla ezmez. Alan silmek ayrı, bilinçli bir işlemdir.

$this->fields->ensure([
    ['bundle' => 'portal_news', 'name' => 'source', 'type' => 'text', 'label' => 'Kaynak'],
    ['bundle' => 'portal_news', 'name' => 'summary', 'type' => 'textarea', 'label' => 'Özet'],
]);

Kendi alan türünü yazmak

Bir hizmeti #[CpFieldType] ile işaretleyin; tür kimliği sınıfın statik id() metodundan gelir ve derleme sırasında FieldTypeRegistry'ye toplanır. Bir modülün türü yüklenemezse yalnızca o modülün türleri düşer; çekirdek seti etkilenmez.

#[CpFieldType]
final class PhoneFieldType extends AbstractFieldType
{
    public static function id(): string { return 'phone'; }
    public function label(): string { return 'field.type.phone'; }

    public function normalize(mixed $raw, FieldContext $context): mixed
    {
        $digits = preg_replace('/\D+/', '', (string) $raw);
        return $digits === '' ? null : $digits;
    }

    public function validate(mixed $value, FieldContext $context): array
    {
        return strlen((string) $value) < 10 ? ['field.phone.too_short'] : [];
    }
}

AbstractFieldType güvenli varsayılanlar verir (indekslenemez, ayarsız, her zaman geçerli); yalnızca ihtiyaç duyduğunuz metotları geçersiz kılın:

Metot Görev
normalize Ham girdiyi temiz depolama biçimine çevirir (tek değer üzerinde çalışır)
validate Hata anahtarlarının listesini döndürür (boşsa geçerli)
indexKind / indexValue Düz indekse nasıl yazılacağı (string, int, decimal, datetime)
settingsSchema / normalizeSettings Türün ayar alanları (AACP'de form olarak çıkar)
defaultValue Yeni kayıtta varsayılan

Değer sayısını (cardinality) türünüz değil çağıran kod yönetir; metotlarınız tek değer üzerinde çalışır.

Son güncelleme: 13.10.2026