
# SQL Yansıması

`SqlMirror`, storh koleksiyonlarını SQLite veya MySQL tablolarına aktarır; böylece dosyalar asıl kaynak olarak kalırken SQL içinde birleştirmeler, tablolar arası sıralama, alt dize araması ve raporlama yapabilirsiniz. Bunu veritabanı işlevi de gören ikincil bir indeks gibi düşünebilirsiniz: yansı türetilir, atılabilir ve istenen zamanda yeniden oluşturulabilir. `sqlite` veya `mysql` sürücüsüyle `ext-pdo` yalnızca yansıyı kullandığınızda gerekir; çekirdek paket bağımlılıksız kalır.

## Kurulum

```php
use Storh\DocStore;
use Storh\Schema;
use Storh\SqlMirror;

$schema = Schema::collection('pages')
    ->string('slug')->unique()
    ->string('kind')->index()
    ->int('publishedAt')->range();

$pages  = new DocStore($root, 'pages', schema: $schema);
$events = new Storh\SegmentedLog($root, 'events');

$mirror = new SqlMirror(new PDO('sqlite:' . $root . '/mirror.db'), 'app_');
$mirror->collection($pages, 'pages', $schema);
$mirror->collection($events, 'events');

$mirror->install();
$mirror->push();
```

Her iki motorun da yansıması oluşturulabilir: `DocStore` koleksiyonları ve `SegmentedLog` akışları (yalnızca canlı kayıtlar aktarılır). Bir `Schema` geçirmek isteğe bağlıdır ve depodan bağımsızdır: tanımlanmış `string`/`int`/`float`/`bool` alanları türü belirlenmiş, indeksli sütunlara dönüşür; diğer her şey `data` JSON sütununda sorgulanabilir kalır. `mixed` alanları indeksleme için işaretlenemez.

## Bağlantılar

Oluşturucu; bir `PDO` tanıtıcısı (`sqlite` veya `mysql` sürücüsü otomatik algılanır), bir `mysqli` tanıtıcısı ya da herhangi bir `SqlMirrorConnection` uygulaması kabul eder:

```php
$mirror = new SqlMirror(new PDO('sqlite:' . $root . '/mirror.db'));
$mirror = new SqlMirror(new PDO('mysql:host=127.0.0.1;dbname=app;charset=utf8mb4', $user, $password));
$mirror = new SqlMirror(new mysqli('127.0.0.1', $user, $password, 'app'), 'wp_storh_');
```

mysqli yolu, her iki mysqli hata raporlama modunda da çalışır; dolayısıyla bir barındırıcı uygulamadaki mevcut tanıtıcı (örneğin WordPress'ın `$wpdb->dbh` tanıtıcısı) olduğu gibi yeniden kullanılabilir. Davranış tüm sürücülerde aynıdır; aktarım başarısızlıkları her sürücüde koleksiyonun işlemini geri alır.

Her tabloya `id` (birincil anahtar), `hash` (artımlı aktarımları yönlendirir), türü belirlenmiş sütunlar ve `data` (tam kayıt JSON) eklenir.

## Eşitleme

```php
$mirror->push();                    // reconcile: insert, update, delete as needed
$mirror->push('pages');             // reconcile a single collection
$mirror->flush('pages', $ids);      // push or remove just these ids
$mirror->pull();                    // write mirror rows back into the store
$mirror->verify();                  // report missing, stale, and orphaned rows
$mirror->rebuild();                 // clear all rows and push from scratch
$mirror->uninstall();               // drop the mirror tables
```

`push()`, kayıt özetlerini yansıyla karşılaştırır ve her koleksiyonu tek bir işlemde yazar; böylece çöken veya başarısız olan bir aktarım hiçbir zaman yarım uygulanmış bir toplu işlem bırakmaz. Değişmeyen kayıtların maliyeti, bir kayıt taramasına ek olarak bir `SELECT` işlemidir. Düzenli bir `push()` (örneğin bir cron tetiklemesinden) yansının güncel kalmasını sağlar. Okuyucuların bir istek sırasında yazdığınız kimlikler için yansıda kendi yazdıklarını okuyabilmesi gerekiyorsa `flush()` çağrısını kullanın.

## Sorgulama

Yansıyı yalın SQL ile sorgulayın. `table()`, yansıtılan tablo adını döndürür:

```php
$pdo->query(
    'SELECT p.slug, COUNT(*) AS sales
     FROM ' . $mirror->table('purchases') . ' o
     INNER JOIN ' . $mirror->table('products') . ' p ON p.id = o.product_id
     GROUP BY p.slug
     ORDER BY sales DESC'
);
```

## Dosyalara geri aktarma

`pull()` yansı satırlarını okur ve kimlik sırasına göre kayıtlı depoya yazar. İki kullanım biçimini kapsar:

- Geri yükleme: dosyalar kaybolmuştur ama yansı duruyordur. Aynı koleksiyon adıyla yeni bir depo kaydedip geri aktarın.
- Başlangıç verisi yükleme: harici araçlarla yansı tablosuna yüklenen satırlar storh kayıtlarına dönüşür. Satırlarda geçerli UUIDv7 kimlikleri ve `data` sütununda bir nesne bulunmalıdır. `data` kaydın tamamını içerdiğinden geri aktarımda türü belirlenmiş sütunlar yok sayılır.

```php
$restored = new Storh\DocStore($root, 'pages');
$mirror   = new SqlMirror($pdo);
$mirror->collection($restored, 'pages');
$mirror->pull();          // ['written' => n, 'unchanged' => m]
```

Geri aktarılan yazmalar normal depo işleyişinden geçer; dolayısıyla şema doğrulaması ve benzersiz indeksler uygulanır, ayrıca her kayıt kalıcı olarak yazılır. `pull()` yerel kayıtları hiçbir zaman silmez ve verisi yansı satırıyla zaten eşleşen kayıtları atlar. Elle eklenen satırlara başlangıç verisi yükledikten sonra `push()` komutunu bir kez çalıştırın; bu, özetlerinin eşleşmesi için satırları standart biçimde yeniden yazar.

## Sözleşme ve sınırlar

- Dosyalar asıl kaynaktır. Yansı salt okunur çıktıdır; elle yazılan her şey bir sonraki `push()` sırasında üzerine yazılır veya silinir.
- Yansıda benzersiz olarak işaretlenmiş sütunlar, yazma anında denetim değil, ek güvence sağlar: dosyaların kabul ettiği bir yinelenen değer, başarısız olan ve bütünüyle geri alınan bir aktarıma yol açar. Yazma anında denetim için storh benzersiz indekslerini kullanın.
- Yansı, `flush()` ile açıkça belirttiğiniz kimlikler dışında dosyaların bir aktarma aralığı kadar gerisinde kalır.
- Yansılama, storh sistemine işlem eklemez. Birden çok kaydı atomik olarak yazma işi, kayıt sistemi olarak kullanılan gerçek bir veritabanına aittir.
- Şema değişiklikleri, yansı silinip yeniden kurulurken uygulanır: `uninstall()`, `install()`, `push()`.
