
# Filter

Filter styr vilka data som tas med i en ögonblicksbild. Uteslut cachetabeller, ignorera föränderliga tidsstämpelkolumner eller hoppa över temporära rader. Filter tillämpas när ögonblicksbilden tas och påverkar alla efterföljande operationer (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

Styr vilka tabeller som tas med i en ögonblicksbild. Stöder glob-mönster via PHPs `fnmatch()`.

### Uteslut tabeller

```php
use Merql\Filter\TableFilter;

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

Alla tabeller som matchar ett uteslutningsmönster utelämnas från ögonblicksbilden. Jokertecknet `*` matchar valfri teckenföljd.

### Inkludera tabeller

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

Endast tabeller som matchar ett inkluderingsmönster tas med. Alla andra utesluts.

### Så fungerar det

Filtrets metod `apply()` tar en lista med tabellnamn och returnerar den filtrerade delmängden:

```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']
```

När både inkluderings- och uteslutningsmönster finns (via separata filterinstanser) utvärderas uteslutning först. En tabell måste undvika alla uteslutningsmönster och matcha minst ett inkluderingsmönster för att tas med.

## ColumnFilter

Tar bort specifika kolumner från raddata när ögonblicksbilden tas. Användbart för föränderliga kolumner som ändras vid varje skrivning och annars skulle orsaka falska diffar.

```php
use Merql\Filter\ColumnFilter;

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

### Vanliga användningsområden

- Tidsstämplar för `updated_at` / `modified_at` som ändras vid varje sparning.
- `revision`-räknare som ökar automatiskt.
- `session_token` eller andra temporära fält.

### Så fungerar det

Filtret tar bort namngivna kolumner från varje rad efter att radnyckeln har beräknats:

```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']
```

Radnyckeln (identiteten) beräknas utifrån den ofiltrerade raden. Det innebär att identitetskolumner aldrig påverkas av kolumnfiltret. Endast data som används för fingeravtryck och diffning filtreras.

## RowFilter

Filtrerar enskilda rader med ett predikat-closure. Closure-funktionen tar emot tabellnamnet och hela radens data och returnerar `true` för att ta med raden eller `false` för att utesluta den.

```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;
});
```

### Så fungerar det

Metoden `shouldInclude()` anropas för varje rad när ögonblicksbilden tas:

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

Uteslutna rader får inget fingeravtryck, lagras inte i ögonblicksbilden och visas inte i diffar eller sammanslagningar.

## Kombinera filter

Skicka flera filter till `capture()`. Högst ett filter av varje typ används:

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

Bearbetningsordning:

1. **TableFilter** körs först och avgör vilka tabeller som ska tas med.
2. För varje tabell läses rader från databasen.
3. **RowFilter** körs för varje rad och utesluter rader som inte matchar.
4. **Radnyckeln** beräknas utifrån den ofiltrerade raden (identitetskolumnerna är intakta).
5. **ColumnFilter** tar bort ignorerade kolumner från raddatan.
6. **Fingeravtrycket** beräknas utifrån den filtrerade raddatan.

Den här ordningen säkerställer att identitetskolumner alltid är tillgängliga när nyckeln beräknas, även om kolumnerna senare tas bort av kolumnfiltret.

## Filter och konsekvens

Samma filter bör användas för alla tre ögonblicksbilder i en sammanslagning (base, ours, theirs). Om olika filter används kan det verka som om rader eller kolumner har lagts till eller tagits bort, trots att de bara har filtrerats på olika sätt.

```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);
```
