# Bildirim Şablonları Nasıl Çalışır

https://dev.wisecp.com/tr/bildirim-sablonlari-nasil-calisir

Platformun gönderdiği her e-posta ve her kısa mesaj, olay ve dil başına diskteki üç dosyadan üretilir. Bir şablon yazarı bu dosyaların sahibidir, tıpkı bir tema yazarının view'ının sahibi olduğu gibi.

## Genel Bakış

Bildirim kodun içinde yazılmaz. Kod bir şeyin olduğuna karar verip olguları teslim eder; sözcükler, işaretleme ve konu satırı `templates/notifications` altında yaşar. Bir kurulum müşterisinin okuduğu metni geliştiriciye ihtiyaç duymadan değiştirebilir, bir modül de kendi sözcüklerini paketleyebilir.

Birim **olaydır** ve `grup/ad` biçiminde yazılır. Her olay, kurulu her dilde üç dosyaya sahiptir: e-posta gövdesi, kısa mesaj gövdesi ve konu satırını taşıyan küçük bir dosya. E-posta gövdesi bir belge değil **parçadır**.

Şablona iki şey ulaşır. Grubun **çözümleyicisi** (resolver) çağıranın verdiğini (bir fatura satırı, bir hizmet kimliği, bir talep) adlandırılmış değişkenlere çevirir. Platform da kurulumun kendi değerlerini üstüne ekler: logolar, renkler, şirket bilgileri ve alıcının profili. Şablon yalnızca gösterir.

## Yapı

### Dosya Düzeni

Üç kök var ve her biri ayrı bir soruyu yanıtlar. Konu satırı ve kısa mesaj, e-posta nasıl görünürse görünsün aynı okunur; tasarımların tamamen dışında yaşarlar. E-posta gövdesi ise onu üreten tasarıma aittir.

```bash
templates/notifications/
├── content/                     # PAYLAŞILAN METİN — tek kopya her tasarıma hizmet eder
│   └── en/invoice/
│       ├── invoice-created.json   # {"subject": "..."}
│       └── invoice-created.txt    # kısa mesaj gövdesi, hiç kabuk yok
├── themes/                      # TASARIM — kurulu tasarım başına bir dizin
│   ├── aurora/                  # ürünle birlikte gelen tasarım
│   │   ├── header.html            # en dıştaki kabuk
│   │   ├── content.html           # gövde yuvası, tek bir {$notifi_body} yer tutucusu
│   │   ├── footer.html            # kapanış kabuğu
│   │   └── en/invoice/
│   │       └── invoice-created.html   # e-posta gövdesi, kabuğun bir PARÇASI
│   └── ledger/                  # ikinci tasarım; olayların yalnız bir kısmını taşıyabilir
├── custom/                      # operatörün kendi düzenlemeleri, tasarım başına bir ağaç
│   └── ledger/en/invoice/invoice-created.html
└── .htaccess                    # bu ağaçtan HTTP ile hiçbir şey sunulmaz
```

Bu yolları elle kurmayın: sırayı bilen tek yer aşağıdaki yardımcılardır. O olay için dosyası olmayan bir tasarım maili yine gönderir: gövde taban tasarımdan gelir, kabuk kendisinin kalır.

- **View::notification_file()**: E-posta gövdesi: `custom/{aktif}` → `themes/{aktif}` → `themes/aurora`. Var olan ilk dosya kazanır.
- **View::notification_content_file()**: Konu satırı ve kısa mesaj, `content/{dil}` altından — tasarım bu işe hiç karışmaz.
- **View::notification_write_path()**: Bir düzenlemenin kaydedildiği yer: taban dışındaki her tasarım için `custom/{aktif}`. O dosyayı silmek varsayılana döndürür.

### Kabuk

E-posta gövdesi tek başına gönderilmez. Platform üç dosyayı tek bir kabukta birleştirir, biten olay gövdesini `notifi_body` değişkenine atar ve kabuğu aynı değişkenlerle üretir. Kabuk da gövdeyle aynı sırayla çözülür; kendi kabuğunu taşımayan bir tasarım maillerini taban kabuğuyla çerçeveler.

- **header + content + footer**: Bu sırayla birleştirilir ve süreç boyunca dil başına önbelleklenir. Her dilde gelen `content.html` tek bir yer tutucudur, yani çerçeve aslında header ile footer'dır.
- **olay gövdesi bir parçadır**: Header bir tabloyu açar ve açık bırakır; olay gövdesi onu sürdürür, footer kapatır. Kendi belgesini açan bir olay gövdesi, hiçbir e-posta istemcisinin yerleştiremeyeceği bir işaretleme üretir.
- **kısa mesaj kabuk almaz**: Kısa mesaj kanalında birleştirme hiç yapılmaz: `.txt` gövdesi logo, footer ve iletişim bloğu olmadan, yazıldığı gibi teslim edilir.
- **konu da render edilir**: `.json` dosyasındaki `subject` aynı motordan ve aynı değişkenlerden geçer, yani yer tutucu taşıyabilir. Yalnızca e-posta kanalında okunur.

### Gruplar ve Çözümleyiciler

Grup aynı anda hem bir dizin hem de yapılandırma dosyasının bir bölümüdür. Değişkenleri hangi çözümleyicinin kuracağını ve alıcının tercihlerinin hangi bildirim kategorisine karşı denetleneceğini o belirler.

| Grup | Gelen olay | Çözümleyici | Tercih kategorisi |
| --- | --- | --- | --- |
| `invoice` | 10 | `resolve_invoice_context` | faturalar |
| `service` | 11 | `resolve_service_context` | ürün |
| `order` | 4 | `resolve_order_context` | ürün |
| `domain` | 13 | `resolve_domain_context` | alan adı |
| `user` | 36 | `resolve_user_context` | genel |
| `user-tickets` | 8 | `resolve_ticket_context` | destek |
| `admin-tickets` | 4 | `resolve_ticket_context` | destek |
| `admin-messages` | 18 | `resolve_admin_message_context` | genel |
| `sms-intl` | 3 | `resolve_sms_intl_context` | ürün |
| `newsletter` | 2 | yok | dispatch edilmez, aşağıya bakın |
| `license-transfer` | 3 | yok | kayıtlı değil, aşağıya bakın |

Son iki satır, bir olayın normal yoldan geçmeden var olabilmesinin iki biçimidir. `newsletter` yapılandırma kayıtlarına sahip ama çözümleyicisi yok, yani bir dispatch `error` döner. Şablonları doğrudan üretilip kuyruğa itilir ve değişkenleri bülten kodu kendisi verir. `license-transfer` ise tam tersi: üç olay diskte durur ve yapılandırmada hiç bölümü yoktur. Düşük seviyeli giriş noktasından gönderilirler; o giriş noktası ayar bulamadığında e-posta ve kısa mesajı açık kabul eder.

### Teslim Yolu

Tek bir çağrıdan gerçek bir mesaja giden zincir sabittir ve her adım zinciri bitirebilir.

```bash
Notification::dispatch('invoice', 'invoice-created', ['entity' => $invoice])
  1. gate:notification.dispatch          bir dinleyici veto edebilir  -> blocked
  2. notifications/invoice/invoice-created okunur                     -> yoksa/kapalıysa disabled
  3. grubun çözümleyicisi: entity -> değişkenler + user_id            -> boş dönerse error
  4. alıcının tercih bitmask'i kategoriye karşı denetlenir            -> opted_out
  5. alıcı listesi kurulur (sahip, ek adresler, yöneticiler)          -> boşsa no_recipients
  6. alıcı BAŞINA render: o alıcının dili ve kanalıyla
  7. olay sync listesindeyse hemen gönderilir, değilse kuyruğa yazılır -> sent | queued
  8. panel içi satırlar yazılır: alıcının kendi satırı ve yönetici kopyası
  9. action:notification.dispatched
```

Şablon 6. adımda, dispatch başına değil **alıcı başına** bir kez okunur: aynı olayın iki alıcısı farklı dillerde ve farklı kanallarda okuyor olabilir. 7. adımdaki eşzamanlı liste kısadır ve güvenlik açısından kritik olayları taşır; geri kalan her şey kuyruğu bekler.

Bir ad 1. adımdan önce değişir. E-posta aktarımıyla açılan talepte `ticket-replied-by-admin`, `ticket-replied-by-admin-pipe` olur. Zincirdeki her kanca değişen adı görür; adıyla süzen dinleyici iki adı da tanımalıdır. 8. adımda müşteri için yazılan panel içi satır istenen adı korur.

## Referans

### İki Giriş Noktası

```php
public static function dispatch(string $group, string $name, array $context = []): array;
public static function send(array $params): array|string|bool;
public static function get_recipients(string $group, string $name, array $context = []): array;
```

- **Notification::dispatch()**: Kullanılacak olan budur. Açma/kapama anahtarına, alıcının tercihlerine ve kapı kancasına saygı gösterir; ne olduğunu `status` alanında söyleyen bir dizi döner.
- **Notification::send()**: Düşük seviyeli olan. Tek bir dizi alır ve çözümleyiciyi tümüyle atlar, yani değişkenleri **siz** verirsiniz. Şablonsuz gövde, ham adres listesi ve kuyruk işçisinin hazır bir satırı teslim etmesi meşru kullanımlarıdır.
- **Notification::get_recipients()**: Bir dispatch'in ilk yarısını aynı argümanlarla çalıştırıp durur: yazmak yerine kime yazılacağını döner. Yeni bir olay bağlanırken işe yarar.

### Bir Dispatch Ne Döner

Asla bir boolean değil, istisna da fırlatmaz. Dizi her zaman `status` taşır; başarılı olan ayrıca `batch_id` ve `items` altında alıcı başına bir kayıt taşır.

| status | Anlamı | Nerede kararlaşır |
| --- | --- | --- |
| `queued` | Kuyruğa satır yazıldı, teslim bir dakika içinde gelir. | normal yol |
| `sent` | Yerinde teslim edildi; olay senkron listesinde ya da çağıran zorladı. | normal yol |
| `blocked` | Bir dinleyici olayı veto etti. | dispatch kapı kancası |
| `disabled` | Olayın yapılandırma kaydı yok ya da anahtarı kapalı. Kayıtsız bir olayın verdiği yanıt budur. | yapılandırma dosyası |
| `error` | Grubun çözümleyicisi yok, çözümleyici boş döndü (bulunmayan fatura, silinmiş müşteri) ya da dispatch içinde bir adım hata verdi. Hata, hata kaydına yazılır ve fırlatılmaz; yanıt `message` ile `partial` taşır. | çözümleyici haritası ya da dispatch'in içi |
| `opted_out` | Alıcının tercih bitmask'i grubun kategorisini dışarıda bırakıyor. | alıcının profili |
| `no_recipients` | Kanal anahtarları ve kanal-bazlı tercihler uygulandıktan sonra kimse kalmadı. | alıcı listesi kurucusu |

`partial`, yeniden deneyen bir çağırana denemenin güvenli olup olmadığını söyler. `false`: hatadan önce hiçbir şey çıkmadı, yeniden dispatch bildirimi bir kez gönderir. `true`: bir mesaj ya da panel içi satır zaten çıktı, yeniden dispatch o kısmı tekrarlar.

### Şablon Motoru

Dosyaları hangi motorun ayrıştıracağı kurulum geneli bir ayardır ve `options/notification-template-engine` anahtarından okunur. Smarty olarak gelir ve ağaçtaki her şablon ona göre yazılmıştır. İki değer daha vardır: Twig ve yalnızca `{ad}` biçimini anlayan düz dize değiştirme kipi.

Bu motor tema kum havuzu değildir ve onun gibi davranmaz:

- **otomatik kaçış yok**: Değerler geldikleri gibi, işaretleme dahil yazılır. Tema varsayılan olarak kaçış uygular; bu motor uygulamaz. Müşterinin yazdığı her şeyde escape değiştiricisini kullanın.
- **kısıtlı bir politika**: Şablondan on üç platform sınıfı çağrılabilir ve otuz dokuz dil fonksiyonuna izin verilir. Bunun dışındaki her şey derleme hatasıdır; derleme hatasının sonuçları için tuzaklara bakın.

### Kuyruk

- **NotificationQueue::add()**: Hazır tek bir satır alır: kanal, alıcı, konu, gövde, ekler, öncelik, toplu iş kimliği, isteğe bağlı zamanlama. Kimliğini döner. Dispatch alıcı başına bir satır yazar.
- **NotificationQueue::process()**: Tek bir satırı e-posta ya da kısa mesaj modülü üzerinden teslim eder. Panelin elle yeniden deneme butonu da aynı kod yolunu kullanır. Elle başarısız olan bir satır arka planda da aynı şekilde başarısız olur.
- **zamanlanmış boşaltma**: Her dakika çalışır, bir çökmeyle askıda kalan satırları geri alır ve tur başına en çok iki yüz satır dağıtır. Gövde dispatch anında üretildiği için, bir şablonu düzenlemek kuyrukta bekleyen mesajı değiştirmez.

## Tuzaklar

> **Eksik dil dosyası hata değil sessizliktir**
> 
> Yol alıcının dilinden kurulur ve geri düşme yoktur. Dosya yoksa hiçbir şey üretilmez ve alıcı atlanır. Dispatch geri kalan herkes için yine başarı bildirir. Gelen ağaçta ölçüldü: sekiz olay İngilizcede var, Almancada yok. Almanca okuyan bir müşteri o olaylar için hiçbir şey almaz.

> **Tek bir bozuk etiket dosyanın tamamını işlenmemiş bırakır**
> 
> Derleme ya hep ya hiçtir ve hata yakalanıp ham kaynakla yanıtlanır. Yoldan çıkmış tek bir yer tutucu kendi satırında kalmaz: aynı dosyadaki diğer bütün yer tutucular da işlenmeden gider ve müşteri görünür süslü parantezlerle dolu bir mesaj alır. Uyarı operatöre değil loga düşer.

> **Taban tasarımda ayrı bir bindirme katmanı yoktur**
> 
> Taban tasarım ürünle birlikte gelir; o etkinken yapılan bir düzenleme doğrudan paketlenmiş dosyanın üzerine yazılır ve sonraki güncelleme onu değiştirir. Başka her tasarım `custom/` altına kaydeder, oraya güncelleme dokunamaz.

> **Yalnız dosyalar bir olay etmez**
> 
> Üç dosya eklemek size hiçbir şey vermez. Yapılandırma kaydı olmadan anahtar bulunamaz ve dispatch `disabled` döner. Çözümleyici kaydı olmadan değişkenler hiç var olmaz, etiket olmadan panel ham anahtarı listeler. Üçlü, birbiriyle uyuşması gereken birkaç yerden yalnızca biridir.

## İlgili Makaleler

- [E-posta Şablonu Yazma](https://dev.wisecp.com/tr/e-posta-sablonu-yazma)
- [SMS Şablonu Yazma](https://dev.wisecp.com/tr/sms-sablonu-yazma)
- [Bildirim Şablonu Değişkenleri](https://dev.wisecp.com/tr/bildirim-sablonu-degiskenleri)
- [Mail Modülü Yazma](https://dev.wisecp.com/tr/mail-modulu-yazma)
- [SMS Modülü Yazma](https://dev.wisecp.com/tr/sms-modulu-yazma)
- [Kanca Dinleyicisi Yazma](https://dev.wisecp.com/tr/kanca-dinleyicisi-yazma)
