# Tema Performansı ve Önbellek

https://dev.wisecp.com/tr/tema-performansi-ve-onbellek

Tema her sayfada çalışır, yani iki kez hesapladığı şeyin bedeli iki kez ödenir. Önbellek yardımcısı bunu durdurur; doğruluğu anahtar belirler.

## Genel Bakış

Temalar yapısı gereği ucuzdur: şablon değerleri gösterir, onları controller üretir. Temanın kurulumu yavaşlatabildiği tek yer `hooks.php`'dir.

Çaresi tek bir çağrıdır: kova, anahtar, ömür ve üretici. Doğruluğu iki şey belirler: anahtar ve dışarıda bıraktığınız şey.

## Ön Koşullar

- Çalışan bir tema. `hooks.php` size yabancıysa önce [Tema Kancaları ve Çıktı Filtreleri](https://dev.wisecp.com/tr/tema-kancalari-ve-cikti-filtreleri)ni okuyun.
- `filter:template.variables` için tema başına tek handler; yalnız son dönen değer saklanır.
- Açılacak bir şey yok; önbellek ayarı yardımcının içinde okunur.

## Yapı

### İşin Ait Olduğu Yer

| İhtiyaç | Ait olduğu yer | Ne sıklıkla çalışır |
| --- | --- | --- |
| Tek sayfanın verisi (bu hizmetin faturaları) | Controller; sayfa verisiyle gönderilir | Bir kez ve yalnız o sayfada. |
| Her sayfanın ihtiyacı olan veri (footer kategorileri, para birimi şeridi) | Temanın `hooks.php`'si; değişken filtresi üzerinden | Her website isteğinde. Temanın istek başına maliyet yarattığı tek yer. |
| Sunum (döngüler, koşullar, biçimlendirme) | Şablon | Her sayfada ve bedava kalmak zorunda. Sorgu yapan şablonun hiç önbelleği yoktur. |

### Anahtara Ne Girer

Anahtar sözleşmenin tamamıdır. Değerin bağlı olduğu her şey içinde görünmelidir.

| Parça | Nereden okunur | Eksik bırakmak neyi üretir |
| --- | --- | --- |
| Para birimi | `Money::getUCID()` | Önbelleği ilk ısıtan kişinin sembolüyle biçimlenmiş fiyatlar. |
| Dil | `Language::selected()` | Her yerde tek dilde başlıklar ve bağlantılar. Bağlantılar seçili dil üzerinden kurulur. |
| Sorgunun kapsamı | Geçtiğiniz kategori, ürün ya da menü id'si | Tek kapsamın satırlarının her kapsama servis edilmesi. Sonradan parametre eklerken unutulan parça. |
| Ziyaretçi kimliği | Hiçbir yerden. Paylaşılan bir önbellekte asla yeri yoktur. | Ziyaretçi başına anahtar önbellek değil sızıntıdır. Ziyaretçiye özel değerler hiç önbelleklenmez. |

## Adım Adım

### 1. Tekrarlanan İşi Bulun

1. Handler'ın ne döndürdüğüne değil ne çağırdığına bakın; düz bir getter düğüm başına sorgu açabilir.
2. Değerin nasıl değiştiğini sorun. Katalog verisi ve menüler bir saatlik bayatlığı kaldırır; stok ve sepet kaldırmaz.
3. Değerin kime ait olduğunu sorun. Cevap bir kişiyi adlandırıyorsa hiçbir anahtar onu önbelleklenebilir yapmaz.

### 2. Üreticiyi Sarın

1. Pahalı işi bir closure'a koyun. Önce hesaplayıp sonra önbelleklemek işi her seferinde çalıştırır.
2. Anahtarı yukarıdaki parçalardan sabit bir sırada kurun ve başına temanıza özel bir ön ek koyun.
3. Bir ömür seçin: operatörün düzenlediği veri için bir saat, referans verisi için bir gün. Sıfır hiç sona ermez.
4. Aynı değer bir istekte defalarca isteniyorsa statik bir koruma ekleyin.

```php
$ucid = (int) Money::getUCID();

$rows = Cache::remember('website', 'acme_footer_' . $ucid . '_' . Language::selected(), 3600,
    fn (): array => Products::group_cards($ucid));
```

### 3. Kancaları Önbelleklenen Bölgenin Dışında Tutun

1. Veriyi önbelleğe alın, filtreyi sonuca uygulayın. Üreticinin içinde modülün dinleyicisi önbellek kopyasına donar.
2. Bağlamı önce yerel değişkenlere çıkarın; referanslı çalıştırıcıda literal ölümcül hatadır.
3. Yayınlanan örneği izleyin: yazılım mağazası listesi satırlarını önbelleğe alır, sonra filtreler.

```php
$out = Cache::remember('website', 'software_store_' . $categoryId . '_' . $ucid . '_' . Language::selected(), 3600,
    function () use ($ucid, $categoryId): array {
        // tek sorgu, çok satır, şablonun beklediği biçimde
        return $this->build($categoryId, $ucid);
    });

// Üreticinin DIŞINDA: bu, önbellekli ya da değil, her istekte koşmalıdır.
$ctx = ['currency' => $ucid, 'category' => $categoryId];
Hook::runRefs('filter:product.software_list', $out, $ctx);

return $out;
```

## Referans

### Kanonik Çağrı

```php
// coremio/classes/Cache.php
// Üretici ne döndürüyorsa onu döner: dizi, dize, tam sayı, nesne.
public static function remember(string $name, string $key, int $ttl, callable $producer);
```

- **$name**: Kova. Kova başına tek dosya; birlikte yüklenir ve birlikte temizlenir. Website verisi `website`, menüler `menus` kullanır.
- **$key**: Kova içindeki kayıt ve doğruluk sözleşmesinin tamamı. Başına temanızı, sonra her parçayı sabit sırada ekleyin.
- **$ttl**: Saniye cinsinden ömür. Operatörün düzenlediği veri için 3600, referans verisi için 86400. Sıfır süre sonunu kapatır.
- **$producer**: Herhangi bir çağrılabilir. Iskalamada ve önbellekleme kapalıyken her çağrıda çalışır. Kendi kontrolünüzü eklemeyin.

### Örnek API'si

Nadiren gerekir. Tek bir kaydı incelemek ya da kaldırmak için bunlara uzanın.

```php
// coremio/classes/Cache.php
public static function getInstance(): self;

public function store($key, $data, $expiration = 86400): bool;   // $expiration = 0 hiç sona ermez
public function retrieve($key, $timestamp = false);              // yoksa ya da okunamıyorsa null
public function isCached($key): bool;                            // süresi dolmuşsa kaydı ayrıca düşürür
public function erase($key): self;                               // tek kaydı düşürür
public function eraseAll(): self;                                // bu örneğin işaret ettiği kovayı boşaltır
public function clear($keys = []): void;                         // adlandırılan kovalar, argümansız = tüm kovalar
```

`get()` ve `set()` yoktur. Eksik ya da bozuk kayıt null döner; kanonik çağrı bunu ıskalama sayar.

### Geçersiz Kılma

| Tetikleyici | Çağrı | Neyi kaldırır |
| --- | --- | --- |
| Operatör panelde bir şey kaydetti | Kaydeden operation tarafından zaten yapıldı | Her şeyi. Kaydetme operation'larının çoğu tüm depoyu temizler. |
| Panelin bilmediği bir şeyi önbelleklediniz | `Cache::getInstance()->clear(['kova'])` | Yalnız adı verilen kovaları. Hem yazma hem okuma temanızdaysa kullanın. |
| Zamanın geçmesi | Hiçbiri | Kaydı; ömrü dolduktan sonraki ilk okumada. Süre sonu okuma anında denetlenir. |

> **Tek istek içindeki tekrarlar zaten çözülmüş**
> 
> Kova dosyası istek başına bir kez okunur; üç anahtar tek dosya okumasıdır.

## Örnek

Footer'ında ürün grubu kartları olan bir tema. Üretici önbelleklenir, filtre dışarıda çalışır, şablon yalnız döner.

```php
/*
 * Tema başına TEK handler: view katmanı yalnız son dönen değeri saklar, yani ikinci bir
 * kayıt burada set edilen her anahtarı düşürür. Anahtar ekleyin, ikinci Hook::add eklemeyin.
 */
Hook::add("filter:template.variables", 1, function ($template, $data) {
    $data["footer_groups"] = acme_footer_groups();

    return $data;
});

if (!function_exists('acme_footer_groups')) {
    /**
     * Footer şeridi için ürün grubu kartları. Her website sayfasında koşar, bu yüzden
     * arkasındaki sorgu önbelleklenir; değeri her ziyaretçi paylaşır ve anahtarın onu
     * ziyaretçiye özel kılan iki şeyi taşımak zorunda olmasının sebebi tam olarak budur.
     */
    function acme_footer_groups(): array
    {
        // Aynı istek, birkaç partial: anahtar kurmayı bile atla.
        static $rows = null;
        if ($rows !== null) return $rows;

        // Para birimi ziyaretçiden, bağlantılar seçili dilden gelir.
        // İkisi de anahtara girer, yoksa ilk ziyaretçinin sürümü herkese servis edilir.
        $ucid = (int) Money::getUCID();

        $rows = Cache::remember('website', 'acme_footer_groups_' . $ucid . '_' . Language::selected(), 3600,
            fn (): array => Products::group_cards($ucid));

        return $rows;
    }
}
```

```smarty
{if $footer_groups}
    <ul class="footer-links">
        {foreach $footer_groups as $g}
            <li><a href="{$g.link}">{$g.title}</a> <span class="text-body-secondary">{$g.price}</span></li>
        {/foreach}
    </ul>
{/if}
```

Şablon sorgu da biçimlendirme de yapmaz: fiyat zaten biçimlenmiş geldi.

- **Cache::remember()**: Bir temanın ihtiyacı olan tek önbellek çağrısı. Önbellekleme kapalıyken üreticiye düşer.
- **Money::getUCID()**: Ziyaretçinin para birimi id'si. Biçimlenmiş tutar üreten her şeyin anahtarına girer.
- **Language::selected()**: Seçili dil kodu. Metin ya da üretilmiş bağlantı çıkaran her şeyin anahtarına girer.
- **Products::group_cards()**: Yukarıda kullanılan üretici; kendi içinde aynı iki parçayla önbellekler.

## Tuzaklar

> **Para birimsiz anahtar, hiç test etmeyeceğiniz bir makinede yanlıştır**
> 
> Önbelleği ısıtan değer, sayfayı ilk açan kişinin değeridir; yerelde bu hep sizsinizdir. Aynı gerekçe dil için de geçerlidir.

> **Üreticinin içindeki kanca istekte bir değil saatte bir çalışır**
> 
> Closure'ın içindeki filtre yalnız kayıt yeniden kurulurken uygulanır. Modül temizlikten hemen sonra çalışır, bir dakika sonra durur.

> **Referanslı kanca çalıştırıcısı her argümanı referansla alır**
> 
> Buna filtrelenen değerden sonraki bağlam da dahildir: literal ya da cast ölümcül hatadır.

> **Tek bir ziyaretçiye ait olanı asla önbelleklemeyin**
> 
> Stok, sepet içeriği ve girişten sonra okunan hiçbir şey paylaşılan depodan geçmez. İkinci kişi için yanlış olan değer veri ifşasıdır.

> **Değişken filtresini tema başına bir kez kaydedin**
> 
> Yalnız son dönen değer saklanır, yani ikinci kayıt birincinin her anahtarını düşürür. Belirti, şablon değişkenlerinin aynı anda boşalmasıdır.

## İlgili Makaleler

- [Önbellek](https://dev.wisecp.com/tr/onbellek)
- [Tema Kancaları ve Çıktı Filtreleri](https://dev.wisecp.com/tr/tema-kancalari-ve-cikti-filtreleri)
- [Şablon Değişkenleri](https://dev.wisecp.com/tr/sablon-degiskenleri)
- [Menüler ve Navigasyon](https://dev.wisecp.com/tr/menuler-ve-navigasyon)
