# Tema Anatomisi

https://dev.wisecp.com/tr/tema-anatomisi

Bir temanın `theme.php` içinde bildirebileceği her anahtar ve hangi dizinde ne durduğu.

## Genel Bakış

Tema bir dizin ve bir manifesttir: `theme.php` dosyasından dönen düz bir PHP dizisi.

`theme.php` **şemadır**, `config.php` **değerlerdir**. Güncelleme birincisini değiştirir, ikincisine dokunmaz.

## Yapı

### Dizinler

Sayılar dağıtılan `WStyle` temasından alınmıştır.

| Dizin | İçinde ne var | Zorunlu mu |
| --- | --- | --- |
| `theme.php` | Manifest dizisi | Evet |
| `layouts/` | 4 kabuk: default, auth, checkout, invoice | Yalnız `default.*` |
| `views/` | 6 alanda 63 sayfa ve 6 tekil dosya | Yalnız `home.*` |
| `partials/` | Kabukların birleştirdiği 16 parça | Hayır |
| `components/` | Paylaşılan 33 blok, artı 10 bölümlü `home/` dizini | Hayır |
| `tables/` | 7 kolon ön ayarı, daima `.php` | Hayır; yoksa ham kolon |
| `assets/` | `css/`, `js/`, `images/`, favicon, kütüphaneler | Hayır |
| `locale/` | Yazarın varsayılanları, dil ve kapsama göre | Hayır |
| `content/` | Operatör düzenlemeleri, panelden | Hayır. İlk kayıtta oluşur |
| `hooks.php` | Temanın dinleyicileri, açılışta | Hayır |
| `config.php` | Kaydedilmiş ayar değerleri | Hayır. İlk kayıtta oluşur |

`tables/`, Smarty ya da Twig temada bile düz PHP taşır: ön ayar view değildir. Liste bileşeni dosyayı `$table` kapsamdayken dahil eder; dosya adı ön ayarın adıdır.

### views/ dizininin içi

View'lar alana göre gruplanır. Controller önek ve uzantısız yol ister; ikisini de motor ekler.

```bash
views/
├── account/     25   müşteri paneli: pano, hizmetler, faturalar, talepler, ayarlar
├── auth/         6   giriş, kayıt, unutulan parola, sıfırlama, aktivasyon, davet kabul
├── checkout/    14   yapılandırma, sepet, ödeme, sipariş tamamlandı
├── content/     14   bilgi bankası, haberler, iletişim, sözleşmeler
├── products/     3   katalog yüzeyleri
├── page/         1   dosya tabanlı sayfalar: views/page/{slug} adresi /{slug} olur
├── home.tpl          apply_theme'in ısrar ettiği tek view
├── 404.tpl           temanın kendi bulunamadı yüzeyi
├── maintenance.tpl   site kapalıyken render edilir
└── domain.tpl        kamuya açık alan adı arama

render("account/dashboard")  ->  views/account/dashboard.tpl   (smarty)
                             ->  views/account/dashboard.twig  (twig)
                             ->  views/account/dashboard.php   (php)
```

## Referans

### Üst Seviye Anahtarlar

| Anahtar | Tip | Neye karar verir |
| --- | --- | --- |
| `engine` | string | `smarty`, `twig` ya da `php`. View uzantısını ve kum havuzunu belirler; yazılmaması `php` demektir |
| `meta` | array | Katalog kaydı ve çekirdeğin okuduğu davranış işaretleri |
| `status` | string | `ready` ya da `development`. Önizlenebilir ama etkinleştirilemez; yazılmaması hazır demektir |
| `update-url` | string | Sürüm kontrolü adresi. Boşsa ya da `meta.version` yoksa hiç kontrol edilmez |
| `settings` | array | Şema: `groups` ve `fields`. Form ondan üretilir |

### meta Bloğu

Tema listesi, detay paneli ve sürüm kontrolü okur.

- **name**: Görünen ad; locale dosyası ezer.
- **version**: Kurulu sürüm, `installed-version` olarak gönderilir. Eksikse kontrol kapanır.
- **description**: Katalog kartı için tek cümle; locale dosyası ezer.
- **image**: Kart görseli; **tema dizinine göre** çözülür, `assets/` dizinine göre değil.
- **author, provider**: Tek alanın iki adı; ikisi de varsa `provider` kazanır.
- **website, providerUrl**: Yazarın adresi. Burada `website`, `providerUrl`'i yener.
- **commercial, premium, price, period**: İkisinden biri kartı ücretliye çevirir; `price` ve `period` etiketler.
- **official, license, support, updates, features**: Detay paneli satırları; `license` varsayılanı `Open Source`.
- **docsUrl, supportUrl, demoUrl, purchaseUrl, learnMoreUrl**: Detay paneli butonları; her biri yalnız değer verildiğinde görünür.

### meta İçindeki Davranış Seçenekleri

Çekirdek nasıl davranacağına bunlara bakar.

- **signup_minimal**: Boolean. Kayıt view'ı yalnız çekirdek alanları soruyorsa açılır; formda olmayan alanların zorunluluğu uygulanmaz.
- **dashboard_due_soon_alert**: Boolean. Kapalıyken pano şeridi yalnız vadesi geçmiş faturayı taşır; açıkken yaklaşanı da.
- **disabled_routes**: Temanın servis etmediği rota **anahtarları**: o adresler 404 döner ve site haritasından düşer.

### settings.groups

Grup anahtarından iki anahtarlı tanıma giden harita. Gruplar yalnız formu sıralar.

```php
$settings = [
    'groups' => [
        // grup anahtarı    label = temanın locale dosyasındaki anahtar, düz metin DEĞİL
        'appearance' => ['label' => 'grp_appearance', 'icon' => 'bi-palette'],
        'checkout'   => ['label' => 'grp_checkout',   'icon' => 'bi-cart3'],
    ],
];
```

### settings.fields

Ayar anahtarından tanıma giden harita. Anahtar aynı zamanda POST adı, config anahtarı ve view'ların okuduğu ad.

- **type**: Aşağıdaki altıdan biri; gerisi metin alanı olur ve dize kaydeder.
- **group**: Alanın grubunun anahtarı. Grubu bildirilmemiş alan gruplanmadan görünür.
- **label, desc, placeholder**: Temanın locale dosyasındaki anahtarlar; aktif dilde çözülür, yoksa İngilizceye.
- **default**: Operatör kaydedene kadar ve geçersiz gönderimde kullanılan değer.
- **options**: Yalnız select, `değer => etiket anahtarı`. Aynı zamanda izin listesi: bilinmeyen değer varsayılana düşer.
- **depends**: `başka alan => gereken değer` haritası. Her koşul sağlanana kadar satır kapalıdır. Gizli alan da kaydedilir.
- **multilang**: Yalnız text ve textarea. Dil başına bir sekme; kayıt `dil => değer` haritası.
- **rows**: Textarea yüksekliği, varsayılan 4.
- **html, allowed_tags**: Yalnız text ve textarea. `html => true` olmadan etiketler temizlenir; açıkken `allowed_tags`'e göre süzülür.
- **from_logo**: Yalnız color, tam sayı indeks. Alanı marka rengi çiftine katar ve "logodan seç" aksiyonu ekler.

### Alan Tipleri ve Ne Kaydedildiği

| type | Kontrol | Kaydedilen değer |
| --- | --- | --- |
| `switch`, `checkbox` | Açıklamayı etiket alan kutu | Gerçek boolean `true` / `false` |
| `color` | Renk kutusu ve yazılabilir hex kutusu | `'#rrggbb'`. 3-8 hex hanesi, gerisi varsayılana düşer |
| `number` | Sayı girdisi | Tam sayı, cast edilmiş |
| `select` | `options`'tan üretilen açılır liste | Seçilen anahtar, yoksa varsayılan |
| `textarea` | Textarea, `multilang` ise dil sekmeleri | Dize, ya da `dil => dize` haritası |
| başka her şey | Metin girdisi, `multilang` ise dil sekmeleri | Dize, ya da `dil => dize` haritası |

### Manifesti Okumak

```php
// HERHANGİ bir temanın tüm manifesti, klasör adıyla. Ad başına önbelleklenir, yoksa boş dizi.
public static function manifest(string $themeName): array;

// Çalışan tema. Yapılandırılan klasör yoksa Basic'e düşer.
public static function active(): self;

public function meta(): array;                    // manifest['meta']
public function engine(): string;                 // küçük harfe indirilmiş, yoksa 'php'
public function settings(): array;                // manifest['settings']
public function settingsSchema(): array;          // settings() ile AYNI dizi
public function exists(): bool;                   // manifest boş değil VE dizin yerinde

// Değerler.
public function savedConfig(): array;             // config.php, örnek başına önbelleklenir
public function setting(string $key): mixed;      // kayıtlı değer, yoksa alanın varsayılanı, o da yoksa null
public function allSettings(): array;             // her şema alanı, birleştirilmiş, view'ların aldığı hâliyle

public static function is_shipped(?string $name = null): bool;   // ürünle gelen temalardan biri (Basic, WStyle)
public static function engineLabel(string $engine): string;      // 'smarty' -> 'Smarty', panel için
```

`allSettings()` view'ların `$setting` olarak aldığı şeydir: çok dilli alan aktif dilin dizesidir, renk alanı iki kez gelir (`primary_color`, `primary_color_rgb`).

## Örnek

### Bir Alanın Bildirimi

Tanıtım şeridi: zengin metin alanını kapıya bağlayan bir anahtar.

```php
return [
    'meta' => [
        'name'        => 'Acme',
        'version'     => '1.0.0',
        'author'      => 'Acme Ltd',
        'website'     => 'https://acme.example',
        'image'       => 'cover.png',      // theme.php'nin yanında, assets/ altında DEĞİL
        'description' => '',               // locale/en.php kazanır, o yüzden burası boş

        // Davranış bayrakları: katalog değil çekirdek okur.
        'signup_minimal'           => false,
        'dashboard_due_soon_alert' => false,
        'disabled_routes'          => ['references', 'references_detail'],
    ],
    'update-url' => '',                    // boş: güncelleme hiç kontrol edilmez
    'engine'     => 'smarty',
    'status'     => 'ready',
    'settings'   => [
        'groups' => [
            'topbar' => ['label' => 'grp_topbar', 'icon' => 'bi-megaphone'],
        ],
        'fields' => [
            'topbar_enabled' => [
                'type'    => 'switch',
                'group'   => 'topbar',
                'label'   => 'set_topbar_enabled',      // locale/{lang}.php içindeki anahtar
                'desc'    => 'set_topbar_enabled_desc',
                'default' => false,
            ],
            'topbar_text' => [
                'type'      => 'textarea',
                'group'     => 'topbar',
                'label'     => 'set_topbar_text',
                'default'   => '',
                'rows'      => 3,
                'multilang' => true,        // etkin dil başına bir sekme
                'html'      => true,        // olmasa kayıtta her etiket temizlenir
                'depends'   => ['topbar_enabled' => true],
            ],
        ],
    ],
];
```

```php
return [
    // Katalog kartı bu ikisini meta'dan değil buradan okur.
    'name'        => 'Acme',
    'description' => 'A compact storefront theme with a promotional strip.',

    'grp_topbar'              => 'Promotional Strip',
    'set_topbar_enabled'      => 'Show the strip',
    'set_topbar_enabled_desc' => 'Prints a single line above the header on every public page.',
    'set_topbar_text'         => 'Strip content',
];
```

### Aynı Alanın Geri Okunması

Panel yazar, tema okur.

```php
return [
    'topbar_enabled' => true,
    'topbar_text'    => [
        'en' => '<strong>Launch week</strong> 20% off every plan.',
        'tr' => '<strong>Lansman haftası</strong> tüm planlarda %20 indirim.',
    ],
];
```

```smarty
{* $setting.topbar_text zaten aktif dilin dizesidir, harita değil. *}
{if $setting.topbar_enabled}
    <div class="topbar">{$setting.topbar_text nofilter}</div>
{/if}
```

```php
// setting() HAM saklanan değeri döner: çok dilli bir alan burada hâlâ dil => değer haritasıdır.
$strip = Theme::active()->setting('topbar_text');
$lang  = Language::selected() ?: 'en';
$text  = is_array($strip) ? ($strip[$lang] ?? $strip['en'] ?? '') : (string) $strip;

// allSettings() çözülmüş biçimdir; şablonların yukarıdakini hiç yapmamasının sebebi budur.
$resolved = Theme::active()->allSettings();
$text     = (string) ($resolved['topbar_text'] ?? '');
```

## Tuzaklar

> **config.php'yi siz yazmazsınız**
> 
> Kayıt anında üretilir; güncelleme dokunmaz. Elle düzenleme operatör kaydedene kadar yaşar. Varsayılanlar şemada durur.

> **Şemanın bildirmediği bir değer hiçbir view'a ulaşmaz**
> 
> Birleştirilmiş ayarlar kayıtlı dosyadan değil şemadan kurulur, yani başıboş anahtar şablonlara ulaşmaz.

> **Etiketler locale anahtarıdır**
> 
> Cümle yerine `set_topbar_enabled` gösteren ekran, anahtarın locale dosyasında olmadığını söyler.

> **settings() ve settingsSchema() aynı diziyi döner**
> 
> İkisi de manifestin settings bloğunu işlenmemiş hâlde verir. İşlenmiş biçim allSettings()'tir.

> **meta.image tema dizinine göredir, assets/ dizinine göre değil**
> 
> Dağıtılan değer, manifestin yanındaki çıplak `cover.png`'dir. Başa eğik çizgi bozuk kart üretir.

## İlgili Makaleler

- [Tema Motoru](https://dev.wisecp.com/tr/tema-motoru)
- [İlk Temanız](https://dev.wisecp.com/tr/ilk-temaniz)
- [Tema Ayarları](https://dev.wisecp.com/tr/tema-ayarlari)
- [Tema Çevirisi](https://dev.wisecp.com/tr/tema-cevirisi)
- [Tema Kancaları ve Çıktı Filtreleri](https://dev.wisecp.com/tr/tema-kancalari-ve-cikti-filtreleri)
