
# Filtrai

Filtrai nustato, kurie duomenys įtraukiami į momentinę kopiją. Neįtraukite talpyklos lentelių, ignoruokite kintančius laiko žymų stulpelius arba praleiskite laikinas eilutes. Filtrai taikomi fiksuojant momentinę kopiją ir veikia visas tolesnes operacijas (diff, merge, apply).

```php
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Snapshot\Snapshotter;

$snapshotter = new Snapshotter($connection);

$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['sessions', 'cache_*']),
    ColumnFilter::ignore(['updated_at', 'modified_date']),
]);
```

## TableFilter

Nustato, kurios lentelės įtraukiamos į momentinę kopiją. Palaiko glob šablonus naudojant PHP `fnmatch()`.

### Lentelių neįtraukimas

```php
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['sessions', 'cache_*', 'wp_*_log']);
```

Bet kuri lentelė, atitinkanti neįtraukimo šabloną, į momentinę kopiją neįtraukiama. Pakaitos simbolis `*` atitinka bet kokią simbolių seką.

### Lentelių įtraukimas

```php
$filter = TableFilter::include(['posts', 'comments', 'users']);
```

Užfiksuojamos tik lentelės, atitinkančios įtraukimo šabloną. Visos kitos neįtraukiamos.

### Kaip tai veikia

Filtro metodas `apply()` priima lentelių pavadinimų sąrašą ir grąžina filtruotą jo dalį:

```php
$allTables = ['posts', 'comments', 'sessions', 'cache_pages', 'users'];

$filter = TableFilter::exclude(['sessions', 'cache_*']);
$filtered = $filter->apply($allTables);
// ['posts', 'comments', 'users']

$filter = TableFilter::include(['posts', 'users']);
$filtered = $filter->apply($allTables);
// ['posts', 'users']
```

Kai yra ir įtraukimo, ir neįtraukimo šablonų (atskiruose filtrų egzemplioriuose), pirmiausia įvertinamas neįtraukimas. Kad lentelė būtų palikta, ji negali atitikti nė vieno neįtraukimo šablono ir turi atitikti bent vieną įtraukimo šabloną.

## ColumnFilter

Fiksuojant kopiją iš eilučių duomenų pašalinami konkretūs stulpeliai. Tai naudinga kintantiems stulpeliams, kurių reikšmės keičiasi kiekvieną kartą įrašant ir dėl to atsirastų klaidingų skirtumų.

```php
use Merql\Filter\ColumnFilter;

$filter = ColumnFilter::ignore(['updated_at', 'modified_date', 'last_login']);
```

### Dažniausi naudojimo atvejai

- `updated_at` / `modified_at` laiko žymos, kurios keičiasi kaskart įrašant.
- `revision` skaitikliai, kurie didėja automatiškai.
- `session_token` ar kiti laikini laukai.

### Kaip tai veikia

Filtras pašalina nurodytus stulpelius iš kiekvienos eilutės, apskaičiavus eilutės raktą:

```php
$filter = ColumnFilter::ignore(['updated_at']);

$row = ['id' => 1, 'title' => 'Hello', 'updated_at' => '2026-01-15 10:30:00'];
$filtered = $filter->applyToRow($row);
// ['id' => 1, 'title' => 'Hello']
```

Eilutės raktas (tapatybė) apskaičiuojamas iš nefiltruotos eilutės. Tai reiškia, kad stulpelių filtras niekada nepaveikia tapatybės stulpelių. Filtruojami tik duomenys, naudojami kontrolinei sumai ir skirtumams apskaičiuoti.

## RowFilter

Filtruoja atskiras eilutes pagal predikato uždarymą. Uždarymas gauna lentelės pavadinimą ir visus eilutės duomenis, o grąžina `true`, kad eilutė būtų įtraukta, arba `false`, kad ji būtų neįtraukta.

```php
use Merql\Filter\RowFilter;

// Exclude draft posts.
$filter = RowFilter::create(function (string $table, array $row): bool {
    if ($table === 'posts' && ($row['status'] ?? '') === 'draft') {
        return false;
    }
    return true;
});

// Exclude rows with NULL email.
$filter = RowFilter::create(function (string $table, array $row): bool {
    if ($table === 'users') {
        return $row['email'] !== null;
    }
    return true;
});
```

### Kaip tai veikia

Fiksuojant kopiją kiekvienai eilutei iškviečiamas metodas `shouldInclude()`:

```php
$include = $filter->shouldInclude('posts', ['id' => 1, 'status' => 'draft']);
// false
```

Neįtrauktų eilučių kontrolinės sumos neskaičiuojamos, jos neišsaugomos momentinėje kopijoje ir nerodomos skirtumuose ar sujungimuose.

## Filtrų derinimas

Perduokite `capture()` kelis filtrus. Naudojamas ne daugiau kaip vienas kiekvieno tipo filtras:

```php
$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['cache_*']),
    ColumnFilter::ignore(['updated_at']),
    RowFilter::create(fn($table, $row) => ($row['active'] ?? '1') === '1'),
]);
```

Apdorojimo tvarka:

1. **TableFilter** vykdomas pirmiausia ir nustato, kurios lentelės bus užfiksuotos.
2. Iš duomenų bazės nuskaitomos kiekvienos lentelės eilutės.
3. **RowFilter** taikomas kiekvienai eilutei ir pašalina neatitinkančias.
4. **Eilutės raktas** apskaičiuojamas iš nefiltruotos eilutės (tapatybės stulpeliai išsaugomi).
5. **ColumnFilter** pašalina ignoruojamus stulpelius iš eilutės duomenų.
6. **Kontrolinė suma** apskaičiuojama pagal filtruotus eilutės duomenis.

Tokia tvarka užtikrina, kad tapatybės stulpeliai visada būtų prieinami raktui apskaičiuoti, net jei stulpelių filtras juos vėliau pašalina.

## Filtrai ir nuoseklumas

Visoms trims momentinėms kopijoms (base, ours, theirs) sujungimo metu reikėtų naudoti tuos pačius filtrus. Naudojant skirtingus filtrus, eilutės ar stulpeliai gali atrodyti pridėti arba pašalinti, nors iš tikrųjų jie buvo filtruoti skirtingai.

```php
// Use the same filter set for all snapshots.
$filters = [
    TableFilter::exclude(['sessions']),
    ColumnFilter::ignore(['updated_at']),
];

$base   = $snapshotter->capture('base', filters: $filters);
$ours   = $snapshotter->capture('ours', filters: $filters);
$theirs = $snapshotter->capture('theirs', filters: $filters);
```
