
# Referencia PHP API

Úplná referencia všetkých verejných tried a metód v merql.

## Merql (fasáda)

`Merql\Merql` je statický vstupný bod pre bežné operácie. Obaľuje nízkoúrovňové triedy a automaticky spravuje uchovávanie snímok.

```php
use Merql\Connection;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
use Merql\Merql;

$connection = Connection::sqlite(':memory:');
Merql::init($connection, identityRules: new IdentityRuleSet([
    'options' => IdentityRule::natural(['option_name']),
]));
```

### `init(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null): void`

Inicializujte fasádu databázovým pripojením. Musí sa zavolať pred `snapshot()`, `apply()` alebo `applyGuarded()`; zavolajte ju znova na prepnutie databáz, napríklad pri vytváraní snímok ours a theirs z dvoch rôznych kópií. Bez `$driver` zavolá `DriverFactory::create()`, čo vyhodí výnimku pre iné enginy než MySQL a SQLite, ak nie je zaregistrovaný ovládač. Odovzdaním ovládača prepíšete automatické rozpoznanie; množinou pravidiel identity prepíšete identitu riadkov pre jednotlivé tabuľky počas zachytávania snímky.

### `snapshot(string $name, array $tables = []): Snapshot`

Zachyťte aktuálny stav databázy a uložte ho na disk. Odovzdajte pole názvov tabuliek na vytvorenie snímok konkrétnych tabuliek alebo ho vynechajte pre všetky tabuľky.

```php
$snapshot = Merql::snapshot('baseline');
$snapshot = Merql::snapshot('baseline', ['posts', 'users']);
```

### `diff(string $base, string $current): Changeset`

Načítajte dve pomenované snímky a vypočítajte medzi nimi množinu zmien. `init()` nie je potrebný, pretože operácia pracuje s uloženými snímkami.

```php
$changeset = Merql::diff('before', 'after');
```

### `merge(string $base, string $ours, string $theirs): MergeResult`

Trojcestné zlúčenie troch pomenovaných snímok. Vráti výsledok s čistými operáciami a prípadnými konfliktmi. Zlučuje na úrovni stĺpcov: fasáda vytvorí `ThreeWayMerge` bez `CellMergeConfig`, takže zlúčenie na úrovni buniek sa nepoužije. Ak ho potrebujete, použite priamo [`ThreeWayMerge`](#threewaymerge).

```php
$result = Merql::merge('base', 'ours', 'theirs');
```

### `patch(string $base, string $changes): MergeResult`

Dvojcestné zlúčenie: použije zmeny na základ. Ekvivalent `merge($base, $base, $changes)`. Vždy vytvorí čistý výsledok, pretože neexistujú súbežné úpravy. Podobne ako `merge()` funguje iba na úrovni stĺpcov.

```php
$result = Merql::patch('base', 'changes');
```

### `apply(MergeResult $result): ApplyResult`

Vykoná čistý výsledok zlúčenia nad databázou odovzdanou do `init()`. Vyžaduje `init()`. Ak zostanú nevyriešené konflikty, vyhodí `ConflictException`.

Vykonajú sa všetky operácie vo výsledku, takže úplný výsledok `merge()` sa dá čisto použiť iba v databáze, ktorá stále obsahuje základný stav. V databáze, ktorá už obsahuje stav jednej strany, použite výber operácií, ktoré jej chýbajú; pozrite si [Riadené použitie](https://merql.inline0.dev/sk/docs/apply/guarded).

```php
$applied = Merql::apply($result);
echo $applied->rowsAffected();
```

### `plan(string $id, string $base, string $ours, string $theirs, array $metadata = []): MergePlan`

Vytvorte plán zlúčenia pripravený pre UI z troch pomenovaných snímok. Plán obsahuje stabilné
ID operácií, ID skupín zmien, kontext operácií, konflikty, nesúlady schém,
prehľad, metadáta a hash plánu. Zlúčenie prebieha na úrovni stĺpcov, rovnako ako
v `merge()`; na zlúčenie na úrovni buniek odovzdajte výsledok so zlúčenými bunkami do `MergePlanBuilder::build()`.

```php
$plan = Merql::plan('plan-1', 'base', 'ours', 'theirs', [
    'source' => 'review-ui',
]);
```

### `applyGuarded(MergeResult $result, Snapshot $expectedLive): ApplyResult`

Použite čistý výsledok zlúčenia s optimistickými podmienkami pre živé riadky. Aktualizácie a
odstránenia vyžadujú, aby sa aktuálne živé riadky zhodovali s `$expectedLive`; pri vkladaní sa vyžaduje, aby
cieľová identita ešte neexistovala. `$expectedLive` je snímka cieľovej databázy (theirs, keď sa ours povyšuje do databázy, z ktorej bola vytvorená snímka theirs). Akýkoľvek príkaz, ktorý ovplyvní nula riadkov, spôsobí vrátenie transakcie.

```php
use Merql\Snapshot\SnapshotStore;

$applied = Merql::applyGuarded($selectedResult, SnapshotStore::load('theirs'));
```

### `reset(): void`

Vymažte interné inštancie pripojenia a Snapshotter. Používa sa najmä v testoch na resetovanie stavu medzi spusteniami.

---

## Pripojenie

`Merql\Connection` vytvára adaptéry pripojenia s vhodnými predvolenými nastaveniami. Pripojenia PDO používajú výnimky pri chybách, asociatívny režim načítavania, emulované pripravené príkazy sú vypnuté a načítané hodnoty sa prevádzajú na reťazce. Adaptér mysqli normalizuje načítané hodnoty do rovnakého formátu reťazca alebo null.

### `mysql(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): PdoDatabaseConnection`

```php
use Merql\Connection;

$connection = Connection::mysql('127.0.0.1', 'my_app', 'root', 'secret');
```

### `mysqli(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): MysqliDatabaseConnection`

```php
$connection = Connection::mysqli('127.0.0.1', 'my_app', 'root', 'secret');
```

### `sqlite(string $path = ':memory:'): PdoDatabaseConnection`

Vytvorí pripojenie SQLite. Automaticky zapne cudzie kľúče pomocou `PRAGMA foreign_keys = ON`.

```php
$connection = Connection::sqlite('/var/data/app.sqlite');
$connection = Connection::sqlite(':memory:');
```

### `fromDsn(string $dsn, string $username = '', string $password = ''): PdoDatabaseConnection`

Pripojte sa pomocou ľubovoľného reťazca DSN PDO. merql obsahuje ovládače iba pre `mysql` a `sqlite`; pri iných DSN sa pripojenie otvorí, ale `Merql::init()`, `Snapshotter` a aplikátory vyhodia `Unsupported database driver`, pokiaľ nie je ovládač zaregistrovaný pomocou `DriverFactory::register()`.

```php
$connection = Connection::fromDsn('mysql:host=localhost;dbname=app', 'root', 'pass');
```

### `fromPdo(PDO $pdo): PdoDatabaseConnection`

Obaľte existujúcu inštanciu PDO.

### `fromMysqli(mysqli $mysqli, string $charset = 'utf8mb4'): MysqliDatabaseConnection`

Obaľte existujúcu inštanciu mysqli.

### Zmluva adaptéra

Všetky adaptéry implementujú `Merql\Database\DatabaseConnection`:

```php
interface DatabaseConnection
{
    public function driverName(): string;
    public function execute(string $sql, array $params = []): int;
    public function query(string $sql, array $params = []): array;
    public function scalar(string $sql, array $params = []): ?string;
    public function beginTransaction(): void;
    public function commit(): void;
    public function rollBack(): void;
    public function lastInsertId(): string;
}
```

`execute()`, `query()` a `scalar()` prijímajú pozičné parametre `list<scalar|null>`
pre zástupné symboly `?`. `query()` vracia
`list<array<string, scalar|null>>`; hodnoty sa normalizujú do rovnakého
formátu reťazca alebo null v adaptéroch PDO aj mysqli.

---

## Snapshotter

`Merql\Snapshot\Snapshotter` zachytáva stav databázy. Na rozdiel od fasády vám poskytuje úplnú kontrolu nad filtrami a automaticky neukladá zmeny.

### `__construct(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null)`

Ak nie je zadaný, ovládač sa automaticky určí podľa identity pripojenia. Ak má identita riadka konkrétnej tabuľky prednosť pred predvolenými nastaveniami schémy, zadajte `IdentityRuleSet`.

### `capture(string $name, array $tables = [], array $filters = []): Snapshot`

Zachyťte aktuálny stav. Rozsah obmedzíte zadaním názvov tabuliek. Ak chcete vylúčiť tabuľky, stĺpce alebo riadky, zadajte objekty filtrov.

```php
use Merql\Snapshot\Snapshotter;
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Filter\RowFilter;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;

$snapshotter = new Snapshotter(
    $connection,
    identityRules: new IdentityRuleSet([
        'wp_options' => IdentityRule::natural(['option_name']),
    ]),
);

// All tables.
$snapshot = $snapshotter->capture('full');

// Specific tables with filters.
$snapshot = $snapshotter->capture('filtered', ['posts', 'users'], [
    TableFilter::exclude(['cache_*', 'sessions']),
    ColumnFilter::ignore(['updated_at', 'created_at']),
    RowFilter::create(fn(string $table, array $row) => $row['status'] !== 'trash'),
]);
```

### `captureAliased(string $name, array $physicalToCanonicalTableMap, array $filters = []): Snapshot`

Zachyťte fyzické tabuľky pod kanonickými názvami tabuliek. Túto možnosť použite, keď má tá istá logická tabuľka v rôznych prostrediach odlišné fyzické názvy.

```php
$snapshot = $snapshotter->captureAliased('sandbox', [
    'wp_onumia_abcd_posts' => 'wp_posts',
    'wp_onumia_abcd_postmeta' => 'wp_postmeta',
]);
```

Výsledný snímok ukladá kanonické názvy tabuliek a zároveň zachováva fyzickú schému tabuliek, nakonfigurované stĺpce identity, kľúče riadkov a filtre.

### `fromData(string $name, array $tableData): Snapshot` (statická)

Vytvorte snímok z nespracovaných údajov bez pripojenia k databáze. Hodí sa na testovanie alebo pri importovaní údajov z iného zdroja.

```php
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;

$schema = new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']);

$snapshot = Snapshotter::fromData('test', [
    'posts' => new TableSnapshotData($schema, [
        ['id' => '1', 'title' => 'Hello'],
        ['id' => '2', 'title' => 'World'],
    ], ['id']),
]);
```

---

## Snímok

`Merql\Snapshot\Snapshot` je objekt hodnoty iba na čítanie, ktorý predstavuje úplný stav databázy.

```php
$snapshot->name;                    // string: snapshot identifier
$snapshot->tables;                  // array<string, TableSnapshot>
$snapshot->tableNames();            // list<string>
$snapshot->hasTable('posts');       // bool
$snapshot->getTable('posts');       // ?TableSnapshot
```

---

## TableSnapshot

`Merql\Snapshot\TableSnapshot` obsahuje riadky jednej tabuľky, ich odtlačky a schému.

```php
$table = $snapshot->getTable('posts');

$table->schema;                    // TableSchema
$table->identityColumns;           // list<string>: columns used for row identity
$table->fingerprints;              // array<string, string>: row key to hash
$table->rows;                      // array<string, array<string, mixed>>

$table->rowKeys();                 // list<string>
$table->rowCount();                // int
$table->hasRow('42');              // bool
$table->getRow('42');              // ?array<string, mixed>
$table->getFingerprint('42');      // ?string
```

---

## TableSnapshotData

`Merql\Snapshot\TableSnapshotData` predstavuje vstupné údaje pre `Snapshotter::fromData()`.

```php
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;

$data = new TableSnapshotData(
    schema: new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']),
    rows: [
        ['id' => '1', 'title' => 'First Post'],
    ],
    identityColumns: ['id'],
);
```

---

## IdentityRule

`Merql\Identity\IdentityRule` určuje stĺpce použité na vytvorenie kľúčov riadkov.

```php
use Merql\Identity\IdentityRule;

$primary = IdentityRule::primary(['id']);
$natural = IdentityRule::natural(['option_name']);
$composite = IdentityRule::composite(['object_id', 'term_taxonomy_id']);
$content = IdentityRule::content(['name', 'value']);

$key = $natural->key(['option_name' => 'siteurl']);
// "siteurl"
```

### `forSchema(TableSchema $schema): IdentityRule` (statická)

Určí predvolené pravidlo identity zo schémy: primárny kľúč, prvý jedinečný kľúč a potom náhradné riešenie založené na obsahu.

### `toArray(): array`

Serializuje pravidlo ako `['type' => string, 'columns' => list<string>]`.

### `fromArray(array $data): IdentityRule` (statická)

Obnoví pravidlo zo serializovaných údajov.

---

## IdentityRuleSet

`Merql\Identity\IdentityRuleSet` uchováva prepísania identity pre konkrétne tabuľky.

```php
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;

$rules = new IdentityRuleSet([
    'wp_options' => IdentityRule::natural(['option_name']),
]);

$rule = $rules->ruleFor($schema);
```

### `with(string $table, IdentityRule $rule): IdentityRuleSet`

Vráti novú množinu pravidiel s pridaným prepísaním pre jednu tabuľku.

### `conflictsFor(string $table, IdentityRule $rule, array $rows): array`

Vráti položky `IdentityConflict`, keď podľa pravidla vytvoria viaceré riadky rovnaký kľúč.

### `toArray(): array`

Serializuje pravidlá tabuliek do poľa bezpečného pre JSON.

### `fromArray(array $data): IdentityRuleSet` (statická)

Obnoví množinu pravidiel zo serializovaných údajov.

---

## SnapshotStore

`Merql\Snapshot\SnapshotStore` ukladá a načítava snímky ako súbory JSON.

### `setDirectory(string $directory): void`

Nastaví adresár úložiska. Predvolená hodnota je `.merql/snapshots`.

### `save(Snapshot $snapshot): void`

Zapíše snímok na disk.

### `load(string $name): Snapshot`

Načíta snímok podľa názvu. Ak sa nenájde, vyvolá `SnapshotException`.

### `exists(string $name): bool`

Skontroluje, či súbor snímku existuje.

### `delete(string $name): void`

Odstráni súbor snímku.

---

## Rozdiely

`Merql\Diff\Differ` porovná dva snímky a vytvorí množinu zmien.

### `diff(Snapshot $base, Snapshot $current): Changeset`

```php
use Merql\Diff\Differ;

$differ = new Differ();
$changeset = $differ->diff($baseSnapshot, $currentSnapshot);
```

### `valuesEqual(mixed $a, mixed $b): bool` (statická)

Porovná dve hodnoty po prevedení na reťazec. NULL sa považuje za samostatnú hodnotu: `NULL === NULL` je pravda, `NULL === ''` je nepravda.

---

## Množina zmien

`Merql\Diff\Changeset` je kolekcia operácií na úrovni riadkov iba na čítanie.

```php
$changeset->inserts();             // list<RowInsert>
$changeset->updates();             // list<RowUpdate>
$changeset->deletes();             // list<RowDelete>
$changeset->isEmpty();             // bool
$changeset->count();               // int: total operations
$changeset->forTable('posts');     // Changeset: filtered to one table
```

---

## RowInsert, RowUpdate, RowDelete

Objekty hodnôt iba na čítanie, ktoré predstavujú jednotlivé zmeny riadkov.

### RowInsert

```php
$insert->table;                    // string
$insert->rowKey;                   // string
$insert->values;                   // array<string, mixed>
```

### RowUpdate

```php
$update->table;                    // string
$update->rowKey;                   // string
$update->columnDiffs;              // list<ColumnDiff>
$update->fullRow;                  // array<string, mixed>: complete current row
$update->columnDiffMap();          // array<string, ColumnDiff>: keyed by column name
```

### RowDelete

```php
$delete->table;                    // string
$delete->rowKey;                   // string
$delete->oldValues;                // array<string, mixed>: values before deletion
```

---

## ColumnDiff

`Merql\Diff\ColumnDiff` predstavuje zmenu jedného stĺpca v rámci aktualizácie riadka.

```php
$diff->column;                     // string
$diff->oldValue;                   // mixed
$diff->newValue;                   // mixed
```

---

## ThreeWayMerge

`Merql\Merge\ThreeWayMerge` je hlavný algoritmus zlúčenia.

### `__construct(?CellMergeConfig $cellMergeConfig = null)`

Ak chcete povoliť zlúčenie na úrovni buniek pre stĺpce TEXT a JSON, zadajte `CellMergeConfig`.

```php
use Merql\Merge\ThreeWayMerge;
use Merql\CellMerge\CellMergeConfig;

// Default: column-level merge only.
$merge = new ThreeWayMerge();

// With cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
```

### `merge(Snapshot $base, Snapshot $ours, Snapshot $theirs): MergeResult`

Vykoná trojcestné zlúčenie. Zmeny vypočíta interne (od základu po naše a od základu po ich) a zlúči ich.

```php
$result = $merge->merge($baseSnapshot, $oursSnapshot, $theirsSnapshot);
```

### `patch(Snapshot $base, Snapshot $changes): MergeResult`

Skratka na dvojcestné zlúčenie. Ekvivalent `merge($base, $base, $changes)`.

### `schemaMismatches(): array`

Vráti `SchemaException[]` pre všetky rozdiely v schéme zistené počas posledného zlúčenia (pridané alebo odstránené stĺpce medzi snímkami).

---

## MergeResult

`Merql\Merge\MergeResult` je objekt hodnoty iba na čítanie, ktorý obsahuje výsledok zlúčenia.

```php
$result->isClean();                // bool: no conflicts
$result->operations();             // list<MergeOperation>
$result->conflicts();              // list<Conflict>
$result->operationCount();         // int
$result->conflictCount();          // int
$result->baseSnapshot();           // ?Snapshot
$result->schemaMismatches();       // list<SchemaException>
$result->hasSchemaMismatches();    // bool
```

---

## MergePlan

`Merql\Plan\MergePlan` je serializovateľný model na kontrolu zlúčenia.

```php
$plan->id;                        // string
$plan->baseSnapshot;              // string
$plan->oursSnapshot;              // string
$plan->theirsSnapshot;            // string
$plan->summary;                   // MergePlanSummary
$plan->changeGroups;              // list<MergePlanChangeGroup>
$plan->operations;                // list<MergePlanOperation>
$plan->conflicts;                 // list<MergePlanConflict>
$plan->schemaMismatches;          // list<string>
$plan->hash;                      // string
$plan->metadata;                  // array<string, mixed>
```

Použite `MergePlanSerializer::toJson()` a `::fromJson()` na stabilné spiatočné prevody JSON.

## Výbery

`ChangeGroupSelection` uchováva ID pripravených skupín zmien. Pri vytváraní výsledkov zlúčenia alebo plánov vrátenia vybraných zmien ho preveďte na `OperationSelection`.

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;

$selection = ChangeGroupSelection::fromIds([$groupId]);
$operationSelection = $selection->toOperationSelection($plan);

$selected = (new SelectedMergeResultFactory())
    ->fromOperationSelection($plan, $operationSelection);
```

## RollbackPlan

`Merql\Rollback\RollbackPlan` uchováva opačné operácie a očakávania týkajúce sa odchýlok pre vybranú aplikáciu zmien.

```php
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Rollback\RollbackPlanSerializer;

$rollback = (new RollbackPlanBuilder())->build(
    'rollback-1',
    $plan,
    $operationSelection,
    $liveBeforeRows,
);

$json = RollbackPlanSerializer::toJson($rollback);
```

Pred vrátením zmien použite `RollbackDrift` a potom spustite opačné operácie pomocou `RollbackPlanApplier`.

---

## MergeOperation

`Merql\Merge\MergeOperation` je jedna vyriešená operácia zo zlúčenia.

```php
$op->type;                         // string: 'insert' | 'update' | 'delete'
$op->table;                        // string
$op->rowKey;                       // string
$op->values;                       // array<string, mixed>
$op->source;                       // string: 'ours' | 'theirs' | 'merged'
```

Typové konštanty:

```php
MergeOperation::TYPE_INSERT;       // 'insert'
MergeOperation::TYPE_UPDATE;       // 'update'
MergeOperation::TYPE_DELETE;       // 'delete'
```

---

## Konflikt

`Merql\Merge\Conflict` predstavuje nevyriešený konflikt zlúčenia.

```php
$conflict->table();                // string
$conflict->rowKey();               // string
$conflict->type();                 // string: 'update_update' | 'update_delete' | 'delete_update' | 'insert_insert'
$conflict->column();               // ?string (null for structural conflicts)
$conflict->oursValue();            // mixed
$conflict->theirsValue();          // mixed
$conflict->baseValue();            // mixed
$conflict->primaryKey(['id']);     // array<string, string>: decoded from rowKey
```

### Typy konfliktov

| Typ | Význam |
|---|---|
| `update_update` | Obe strany zmenili ten istý stĺpec na odlišné hodnoty |
| `update_delete` | Naša strana aktualizovala riadok, ktorý ich strana odstránila |
| `delete_update` | Naša strana odstránila riadok, ktorý ich strana aktualizovala |
| `insert_insert` | Obe strany vložili riadok s rovnakým primárnym kľúčom |

---

## ConflictResolver

`Merql\Merge\ConflictResolver` použije zásady riešenia na všetky konflikty vo výsledku zlúčenia.

### `resolve(MergeResult $result, ConflictPolicy $policy): MergeResult` (statická)

Vráti nové `MergeResult` s konfliktmi vyriešenými podľa zásad.

```php
use Merql\Merge\ConflictResolver;
use Merql\Merge\ConflictPolicy;

// Accept all "theirs" values when conflicts arise.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);

// Accept all "ours" values.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::OursWins);

// Return unchanged (for manual inspection).
$resolved = ConflictResolver::resolve($result, ConflictPolicy::Manual);
```

---

## ConflictPolicy

`Merql\Merge\ConflictPolicy` je enum s podkladovým typom a tromi prípadmi.

```php
use Merql\Merge\ConflictPolicy;

ConflictPolicy::OursWins;          // Resolve all conflicts in favor of "ours"
ConflictPolicy::TheirsWins;        // Resolve all conflicts in favor of "theirs"
ConflictPolicy::Manual;            // Leave conflicts unresolved for manual handling
```

---

## ColumnMerge

`Merql\Merge\ColumnMerge` vykonáva logiku zlúčenia po stĺpcoch pre jeden riadok. Interne ho používa `ThreeWayMerge`.

### `merge(string $table, string $rowKey, array $base, array $ours, array $theirs, ?CellMergeConfig $cellMergeConfig = null, array $columnTypes = []): array` (statická)

Vráti `['values' => array, 'conflicts' => list<Conflict>]`.

```php
use Merql\Merge\ColumnMerge;

$result = ColumnMerge::merge(
    'posts',
    '42',
    ['title' => 'Hello', 'status' => 'draft'],     // base
    ['title' => 'Hello', 'status' => 'published'],  // ours
    ['title' => 'New Title', 'status' => 'draft'],   // theirs
);

// $result['values'] = ['title' => 'New Title', 'status' => 'published']
// $result['conflicts'] = [] (clean merge)
```

---

## Aplikátor

`Merql\Apply\Applier` vykoná výsledok zlúčenia v databáze.

### `__construct(DatabaseConnection $connection, ?Driver $driver = null)`

### `apply(MergeResult $result, ?Snapshot $base = null): ApplyResult`

Všetky operácie vykoná v jednej transakcii. Pri akejkoľvek chybe transakciu vráti späť a chybu oznámi v `ApplyResult::errors()` namiesto jej vyvolania. Ak výsledok obsahuje nevyriešené konflikty, vyvolá `ConflictException`.

```php
use Merql\Apply\Applier;

$applier = new Applier($connection);
$applied = $applier->apply($result);

echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
    print_r($applied->errors());
}
```

Poradie cudzích kľúčov sa spracúva automaticky: vkladania sa zoradia podľa závislostí a odstraňovania v opačnom poradí závislostí.

---

## ApplyResult

`Merql\Apply\ApplyResult` je objekt hodnoty iba na čítanie, ktorý vracia aplikátor.

```php
$applied->rowsAffected();         // int
$applied->errors();               // list<string>
$applied->hasErrors();            // bool
```

Po vrátení aplikácie zmien späť `rowsAffected()` stále počíta príkazy spustené pred zlyhaním ([#4](https://github.com/inline0/merql/issues/4)). Skôr než tomuto počtu dôverujete, skontrolujte `hasErrors()`.

---

## DryRun

`Merql\Apply\DryRun` vytvára ľuďom čitateľné SQL bez vykonania čohokoľvek.

### `generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array` (statická)

Vráti zoznam reťazcov SQL so vloženými hodnotami (len na účely zobrazenia).

```php
use Merql\Apply\DryRun;

$statements = DryRun::generate($result);

foreach ($statements as $sql) {
    echo "{$sql};\n";
}

// Output:
// INSERT INTO `posts` (`id`, `title`) VALUES ('10', 'New Post');
// UPDATE `posts` SET `title` = 'Updated' WHERE `id` = '5';
// DELETE FROM `comments` WHERE `id` = '3';
```

---

## SqlGenerator

`Merql\Apply\SqlGenerator` vytvára parametrizované príkazy SQL. Interne ich používajú `Applier` aj `DryRun`.

### `generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array` (statická)

Vráti `list<array{sql: string, params: list<scalar|null>}>`.

```php
use Merql\Apply\SqlGenerator;

$statements = SqlGenerator::generate($result);

foreach ($statements as $stmt) {
    $connection->execute($stmt['sql'], $stmt['params']);
}
```

---

## CellMergeConfig

`Merql\CellMerge\CellMergeConfig` nastavuje stratégie zlúčenia na úrovni buniek pre konkrétne stĺpce alebo typy stĺpcov.

### `auto(): self` (statická)

Továrenská metóda, ktorá priradí `TextCellMerger` stĺpcom TEXT/LONGTEXT a `JsonCellMerger` stĺpcom JSON.

```php
use Merql\CellMerge\CellMergeConfig;

$config = CellMergeConfig::auto();
```

### `forColumn(string $column, CellMerger $merger): self`

Priradí zlúčovač konkrétnemu stĺpcu. Pre kvalifikované názvy použite `table.column` alebo zadajte iba `column` pre všetky tabuľky.

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\TextCellMerger;

$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('description', new TextCellMerger());
```

### `forType(string $typePattern, CellMerger $merger): self`

Priradí zlúčovač všetkým stĺpcom zodpovedajúcim vzoru typu (zhoda podreťazca bez rozlišovania veľkosti písmen).

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;

$config = (new CellMergeConfig())
    ->forType('json', new JsonCellMerger());
```

### `getMerger(string $table, string $column, string $columnType = ''): CellMerger`

Vyhľadá zlúčovač pre danú tabuľku, stĺpec a typ. Priorita: kvalifikovaný názov stĺpca, nekvalifikovaný názov stĺpca, vzor typu, predvolená hodnota (nepriehľadná).

---

## CellMerger (rozhranie)

`Merql\CellMerge\CellMerger` je rozhranie pre stratégie zlúčenia na úrovni buniek.

```php
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

interface CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult;
}
```

K dispozícii sú tri vstavané implementácie.

### TextCellMerger

`Merql\CellMerge\TextCellMerger` zlučuje viacriadkový text pomocou trojcestného porovnania po jednotlivých riadkoch (rovnaký algoritmus, aký git používa pre obsah súborov). Ak obe strany upravia rôzne riadky, zlúčenie prebehne bez konfliktu. Ak obe strany odlišne upravia ten istý riadok, nahlási konflikt.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
$result = $merger->merge(
    "line 1\nline 2\nline 3",
    "line 1\nline 2 modified\nline 3",
    "line 1\nline 2\nline 3 modified",
);
// $result->clean === true
// $result->value === "line 1\nline 2 modified\nline 3 modified"
```

### JsonCellMerger

`Merql\CellMerge\JsonCellMerger` zlučuje hodnoty JSON nezávislým porovnávaním kľúčov najvyššej úrovne. Každý kľúč sa zlúči pomocou trojcestnej logiky. Vnorené objekty sa porovnávajú ako nepriehľadné reťazce JSON (nezlučujú sa rekurzívne).

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
$result = $merger->merge(
    '{"a": 1, "b": 2}',
    '{"a": 10, "b": 2}',        // changed "a"
    '{"a": 1, "b": 20}',         // changed "b"
);
// $result->clean === true
// $result->value === '{"a":10,"b":20}'
```

### OpaqueCellMerger

`Merql\CellMerge\OpaqueCellMerger` je predvolená možnosť. S hodnotami zaobchádza ako s nepriehľadnými reťazcami a pri každom rozdiele vždy nahlási konflikt.

---

## CellMergeResult

`Merql\CellMerge\CellMergeResult` je návratový typ všetkých zlúčovačov buniek.

```php
$result->clean;                    // bool: true if merge succeeded
$result->value;                    // mixed: merged value (or ours on conflict)
$result->conflicts;                // int: number of conflicts

// Factory methods.
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // conflict with count
```

---

## TableSchema

`Merql\Schema\TableSchema` je reprezentácia štruktúry tabuľky iba na čítanie.

```php
use Merql\Schema\TableSchema;

$schema = new TableSchema(
    name: 'posts',
    columns: ['id' => 'int', 'title' => 'varchar(255)', 'content' => 'text'],
    primaryKey: ['id'],
    uniqueKeys: [['slug']],
);

$schema->name;                     // string
$schema->columns;                  // array<string, string>: name to type
$schema->primaryKey;               // list<string>
$schema->uniqueKeys;               // list<list<string>>
$schema->hasPrimaryKey();          // bool
$schema->hasUniqueKeys();          // bool
$schema->columnNames();            // list<string>
```

---

## Systém ovládačov

merql používa zásuvný systém ovládačov na abstrakciu operácií špecifických pre databázy. Ovládače sa automaticky určujú podľa identity ovládača pripojenia.

### Driver (rozhranie)

`Merql\Driver\Driver` definuje zmluvu, ktorú implementujú všetky databázové ovládače.

```php
interface Driver
{
    public function quoteIdentifier(string $name): string;
    public function listTables(DatabaseConnection $connection): array;
    public function readSchema(DatabaseConnection $connection, string $table): TableSchema;
    public function readForeignKeys(DatabaseConnection $connection): array;
    public function selectAll(string $table): string;
}
```

Vstavané implementácie: `MysqlDriver` a `SqliteDriver`.

### DriverFactory

`Merql\Driver\DriverFactory` automaticky určí ovládač podľa pripojenia.

```php
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);
```

Zaregistrovanie vlastného ovládača pre iné databázy:

```php
DriverFactory::register('pgsql', MyPostgresDriver::class);
```

---

## Filtre

### TableFilter

`Merql\Filter\TableFilter` zahŕňa alebo vylučuje tabuľky pomocou vzorov glob.

```php
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['cache_*', 'sessions', 'temp_*']);
$filter = TableFilter::include(['posts', 'users', 'comments']);
```

### ColumnFilter

`Merql\Filter\ColumnFilter` odstraňuje konkrétne stĺpce zo snímkov.

```php
use Merql\Filter\ColumnFilter;

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

### RowFilter

`Merql\Filter\RowFilter` filtruje riadky pomocou predikátovej funkcie.

```php
use Merql\Filter\RowFilter;

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

---

## Výnimky

### SnapshotException

`Merql\Exceptions\SnapshotException` sa vyvolá, keď sa snímok nenájde alebo má neplatný názov.

```php
SnapshotException::notFound('baseline', '/path/to/baseline.json');
```

### MergeException

`Merql\Exceptions\MergeException` sa vyvolá, keď pri zlúčení chýba požadovaný snímok.

```php
MergeException::snapshotRequired('base');
```

### ConflictException

`Merql\Exceptions\ConflictException` sa vyvolá pri pokuse použiť výsledok zlúčenia s nevyriešenými konfliktmi.

```php
ConflictException::unresolved(3);
// Message: "Cannot apply merge with 3 unresolved conflict(s)"
```

### SchemaException

`Merql\Exceptions\SchemaException` sa vyvolá (alebo zhromaždí), keď sa schémy medzi snímkami líšia.

```php
SchemaException::mismatch('posts', 'column "tags" exists in current but not in base');
```

Rozdiely v schéme nezastavia zlúčenie. Zhromaždia sa a možno si ich prezrieť cez `$result->schemaMismatches()`.
